Merge branch 'develop' of https://source.jalview.org/git/jalview into develop
authortcofoegbu <tcnofoegbu@dundee.ac.uk>
Thu, 3 Nov 2016 16:50:29 +0000 (16:50 +0000)
committertcofoegbu <tcnofoegbu@dundee.ac.uk>
Thu, 3 Nov 2016 16:50:29 +0000 (16:50 +0000)
44 files changed:
help/html/misc/aaproperties.html
help/html/webServices/urllinks.html
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/analysis/AAFrequency.java
src/jalview/analysis/Conservation.java
src/jalview/api/AlignViewportI.java
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/IdPanel.java
src/jalview/appletgui/TreeCanvas.java
src/jalview/datamodel/Profile.java [moved from src/jalview/analysis/Profile.java with 59% similarity]
src/jalview/datamodel/ProfileI.java [new file with mode: 0644]
src/jalview/datamodel/ResidueCount.java [moved from src/jalview/analysis/ResidueCount.java with 99% similarity]
src/jalview/datamodel/SequenceGroup.java
src/jalview/gui/Desktop.java
src/jalview/gui/IdPanel.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/Jalview2XML_V1.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/gui/TreeCanvas.java
src/jalview/io/AnnotationFile.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/schemes/ColourSchemeI.java
src/jalview/schemes/FollowerColourScheme.java
src/jalview/schemes/ResidueColourScheme.java
src/jalview/schemes/ResidueProperties.java
src/jalview/util/DBRefUtils.java
src/jalview/util/Format.java
src/jalview/util/UrlConstants.java
src/jalview/util/UrlLink.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/workers/ConsensusThread.java
src/jalview/workers/ConservationThread.java
test/jalview/analysis/AAFrequencyTest.java
test/jalview/analysis/ConservationTest.java [new file with mode: 0644]
test/jalview/datamodel/ResidueCountTest.java [moved from test/jalview/analysis/ResidueCountTest.java with 96% similarity]
test/jalview/gui/PopupMenuTest.java
test/jalview/schemes/ResidueColourSchemeTest.java
test/jalview/schemes/ResiduePropertiesTest.java
test/jalview/util/DBRefUtilsTest.java
test/jalview/util/FormatTest.java
test/jalview/util/UrlLinkTest.java

index 51274d7..b53f6ef 100755 (executable)
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  -->
 <head>
-<title>Amino Acid Properties</title>
+<style>
+body {
+    font-size: 100%;
+}
+table {
+    border: solid;
+    border-collapse: separate;
+}
+th, td {
+    border-style:none;
+    font-family: "Courier New", Courier, mono;
+    font-size: large;
+}
+</style><title>Amino Acid Properties</title>
 </head>
 <body>
   <div align="center">
     <h1>Amino Acid Properties</h1>
     <img src="properties.gif"> <br>
-    <table width="295" border="1" cellspacing="0" cellpadding="0">
-      <tr>
-        <td width="291"><pre>
-            <font size="4" face="Courier New, Courier, mono"><strong>ILVCAGMFYWHKREQDNSTPBZX-</strong>
-XXXXXXXXXXX&middot;&middot;&middot;&middot;&middot;&middot;&middot;X&middot;&middot;&middot;XX Hydrophobic
-&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;XXXXXXXXXX&middot;XXXXX Polar
-&middot;&middot;XXXX&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;XXXXX&middot;&middot;XX Small
-&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;X&middot;&middot;XX Proline
-&middot;&middot;&middot;&middot;XX&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;X&middot;&middot;&middot;&middot;XX Tiny
-XXX&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;XX Aliphatic
-&middot;&middot;&middot;&middot;&middot;&middot;&middot;XXXX&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;XX Aromatic
-&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;XXX&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;XX Positive
-&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;X&middot;X&middot;&middot;&middot;&middot;&middot;&middot;XX Negative
-&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;&middot;XXXX&middot;X&middot;&middot;&middot;&middot;&middot;&middot;XX Charged</font>
-          </pre></td>
-      </tr>
+    <table>
+    <tr><th>ILVCA</th><th>GMFYW</th><th>HKREQ</th><th>DNSTP</th><th>BZX-</th><th></th></tr>
+      <tr><td>XXXXX</td><td>XXXXX</td><td>XX&middot;&middot;&middot;</td><td>&middot;&middot;&middot;X&middot;</td><td>&middot;&middot;XX</td><td>Hydrophobic</td></tr>
+<tr><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;XX</td><td>XXXXX</td><td>XXXX&middot;</td><td>XXXX</td><td>Polar</td></tr>
+<tr><td>&middot;&middot;XXX</td><td>X&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>XXXXX</td><td>&middot;&middot;XX</td><td>Small</td></tr>
+<tr><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;X</td><td>&middot;&middot;XX</td><td>Proline</td></tr>
+<tr><td>&middot;&middot;&middot;&middot;X</td><td>X&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;X&middot;&middot;</td><td>&middot;&middot;XX</td><td>Tiny</td></tr>
+<tr><td>XXX&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;XX</td><td>Aliphatic</td></tr>
+<tr><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;XXX</td><td>X&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;XX</td><td>Aromatic</td></tr>
+<tr><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>XXX&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;XX</td><td>Positive</td></tr>
+<tr><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;X&middot;</td><td>X&middot;&middot;&middot;&middot;</td><td>&middot;&middot;XX</td><td>Negative</td></tr>
+<tr><td>&middot;&middot;&middot;&middot;&middot;</td><td>&middot;&middot;&middot;&middot;&middot;</td><td>XXXX&middot;</td><td>X&middot;&middot;&middot;&middot;</td><td>&middot;&middot;XX</td><td>Charged</td></tr>
     </table>
+    </font>
   </div>
   <p>
     <br> From Livingstone, C. D. and Barton, G. J. (1993), <br>
index 0a4c650..1a1f0d0 100644 (file)
@@ -28,7 +28,7 @@
     and the desktop application are able to open URLs as 'popups' in
     your web browser. <br> Double-clicking on the ID of a sequence
     will open the first URL that can be generated from its sequence ID.
-    This is often the SRS site, but you can easily configure your own <a
+    This is by default the EMBL-EBI site, but you can easily configure your own <a
       href="#urllinks">sequence URL links</a>.
   </p>
   <p>
   <p>
     <strong><a name="urllinks">Configuring URL Links</a></strong> <br>URL
     links are defined in the &quot;Connections&quot; tab of the <a
-      href="../features/preferences.html">Jalview desktop
-      preferences</a>, or specified as <a
-      href="http://www.jalview.org/examples/appletParameters.html#parameters">applet
-      parameters</a>. <br> By default the item &quot;SRS&quot; is added
+    href="../features/preferences.html">Jalview desktop
+    preferences</a>, or specified as <a
+    href="http://www.jalview.org/examples/appletParameters.html#parameters">applet
+    parameters</a>. <br> By default the item &quot;EMBL-EBI Search&quot; is added
     to this link menu. This link will show a web page in your default
     browser with the selected sequence id as part of the URL.<br>
     In the preferences dialog box, click <strong>new</strong> to add a
@@ -51,9 +51,9 @@
     to remove it.<br> You can name the link, this will be displayed
     on a new menu item under the &quot;Link&quot; menu when you right
     click on a sequence id. <br> The URL string must contain a
-    token that can be replaced with a sequence ID. The simplest token is
+    token that can be replaced with a sequence ID or DB accession ID. The simplest token is
     &quot;$SEQUENCE_ID$&quot;, which will be replaced by the chosen
-    sequence id when you click on it.
+    sequence id when you click on it. 
   </p>
   <p>
     eg.<br> UniRef100 =
     Swissprot = http://www.expasy.org/uniprot/$SEQUENCE_ID$ <br> <br>
     Links will also be made for any database cross references associated
     with the sequence where the database name exactly matches a URL link
-    name. In this case, the $SEQUENCE_ID$ string will be replaced with
+    name. In this case, the $DB_ACCESSION$ string will be replaced with
     the accession string for the database cross-reference, rather than
-    the sequence ID for the sequence (<em>since Jalview 2.4</em>).
+    the sequence ID for the sequence (<em>since Jalview 2.10.1</em>).
   </p>
   <p>
     <strong>Regular Expression Substitution</strong><br> A url may
     contain a string of the form $SEQUENCE_ID=/<em>regular
-      expression</em>/=$. In this case, the regular expression will be
-    applied to the full sequence ID string and the resulting match will
+    expression</em>/=$ or $DB_ACCESSION=/<em>regular expression</em>/=$. 
+    In this case, the regular expression will be
+    applied to the full sequence ID or DB accession ID string and the resulting match will
     be inserted into the URL. Groups of parentheses can be used to
     specify which regions of the regular expression will be used to
     generate the URL:
index 864e34a..d8c9ad3 100644 (file)
@@ -787,9 +787,9 @@ label.hide_columns_containing = Hide columns containing
 label.hide_columns_not_containing = Hide columns that do not contain
 option.trim_retrieved_seqs = Trim retrieved sequences
 label.trim_retrieved_sequences = When the reference sequence is longer than the sequence that you are working with, only keep the relevant subsequences.
-label.use_sequence_id_1 = Use $SEQUENCE_ID$ or $SEQUENCE_ID=/<regex>/=$
-label.use_sequence_id_2 = to embed sequence id in URL
-label.use_sequence_id_3 = Use $SEQUENCE_NAME$ similarly to embed sequence name
+label.use_sequence_id_1 = Use $DB_ACCESSION$ or $DB_ACCESSION=/<regex>/=$
+label.use_sequence_id_2 = to embed accession id in URL
+label.use_sequence_id_3 = Use $SEQUENCE_ID$ similarly to embed sequence id
 label.use_sequence_id_4 = 
 label.ws_parameters_for = Parameters for {0}
 label.switch_server = Switch server
@@ -1268,3 +1268,7 @@ status.exporting_alignment_as_x_file = Exporting alignment as {0} file
 label.column = Column
 label.cant_map_cds = Unable to map CDS to protein\nCDS missing or incomplete
 label.operation_failed = Operation failed
+label.SEQUENCE_ID_no_longer_used = $SEQUENCE_ID$ is no longer used for DB accessions
+label.SEQUENCE_ID_for_DB_ACCESSION1 = Please review your URL links in the 'Connections' tab of the Preferences window:
+label.SEQUENCE_ID_for_DB_ACCESSION2 = URL links using '$SEQUENCE_ID$' for DB accessions now use '$DB_ACCESSION$'.
+label.do_not_display_again = Do not display this message again
index 754d94d..4f142b6 100644 (file)
@@ -720,10 +720,10 @@ label.select_columns_containing = Seleccione las columnas que contengan
 label.select_columns_not_containing = Seleccione las columnas que no contengan
 option.trim_retrieved_seqs = Ajustar las secuencias recuperadas
 label.trim_retrieved_sequences = Cuando la secuencia de referencia es más larga que la secuencia con la que está trabajando, sólo se mantienen las subsecuencias relevantes.
-label.use_sequence_id_1 = Utilice $SEQUENCE_ID$ o $SEQUENCE_ID=/<regex>/=$
-label.use_sequence_id_2 = para embeber el id de la secuencia en una URL
-label.use_sequence_id_3 = Utilice $SEQUENCE_NAME$ de manera similar para embeber
-label.use_sequence_id_4 = el nombre de la secuencia
+label.use_sequence_id_1 = Utilice $DB_ACCESSION$ o $DB_ACCESSION=/<regex>/=$
+label.use_sequence_id_2 = para embeber el ID de accesión en una URL
+label.use_sequence_id_3 = Utilice $SEQUENCE_ID$ de manera similar para embeber
+label.use_sequence_id_4 = el ID de la secuencia
 label.ws_parameters_for = Parámetros para {0}
 label.switch_server = Cambiar servidor
 label.open_jabaws_web_page = Abra el página principal del servidor JABAWS en un navegador web
@@ -1269,3 +1269,7 @@ status.exporting_alignment_as_x_file = Exportando alineamiento como fichero tipo
 label.column = Columna
 label.cant_map_cds = No se pudo mapear CDS a proteína\nDatos CDS faltantes o incompletos
 label.operation_failed = Operación fallada
+label.SEQUENCE_ID_no_longer_used = $SEQUENCE_ID$ no se utiliza más para accesiones DB
+label.SEQUENCE_ID_for_DB_ACCESSION1 = Por favor, revise sus URLs en la pestaña 'Conexiones' de la ventana de Preferencias:
+label.SEQUENCE_ID_for_DB_ACCESSION2 = URL enlaza usando '$SEQUENCE_ID$' para accesiones DB ahora usar '$DB_ACCESSION$'.
+label.do_not_display_again = No mostrar este mensaje de nuevo
\ No newline at end of file
index 61e11c4..6bdffe1 100755 (executable)
  */
 package jalview.analysis;
 
-import jalview.analysis.ResidueCount.SymbolCounts;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.Profile;
+import jalview.datamodel.ProfileI;
+import jalview.datamodel.ResidueCount;
 import jalview.datamodel.SequenceI;
+import jalview.datamodel.ResidueCount.SymbolCounts;
 import jalview.ext.android.SparseIntArray;
 import jalview.util.Comparison;
 import jalview.util.Format;
@@ -62,13 +65,13 @@ public class AAFrequency
     }
   }
 
-  public static final Profile[] calculate(List<SequenceI> list,
+  public static final ProfileI[] calculate(List<SequenceI> list,
           int start, int end)
   {
     return calculate(list, start, end, false);
   }
 
-  public static final Profile[] calculate(List<SequenceI> sequences,
+  public static final ProfileI[] calculate(List<SequenceI> sequences,
           int start, int end, boolean profile)
   {
     SequenceI[] seqs = new SequenceI[sequences.size()];
@@ -84,7 +87,7 @@ public class AAFrequency
         }
       }
 
-      Profile[] reply = new Profile[width];
+      ProfileI[] reply = new ProfileI[width];
 
       if (end >= width)
       {
@@ -110,7 +113,7 @@ public class AAFrequency
    *          if true, store all symbol counts
    */
   public static final void calculate(final SequenceI[] sequences,
-          int start, int end, Profile[] result, boolean saveFullProfile)
+          int start, int end, ProfileI[] result, boolean saveFullProfile)
   {
     // long now = System.currentTimeMillis();
     int seqCount = sequences.length;
@@ -170,7 +173,7 @@ public class AAFrequency
       int maxCount = residueCounts.getModalCount();
       String maxResidue = residueCounts.getResiduesForCount(maxCount);
       int gapCount = residueCounts.getGapCount();
-      Profile profile = new Profile(seqCount, gapCount, maxCount,
+      ProfileI profile = new Profile(seqCount, gapCount, maxCount,
               maxResidue);
 
       if (saveFullProfile)
@@ -231,7 +234,7 @@ public class AAFrequency
    *          number of sequences
    */
   public static void completeConsensus(AlignmentAnnotation consensus,
-          Profile[] profiles, int iStart, int width, boolean ignoreGaps,
+          ProfileI[] profiles, int iStart, int width, boolean ignoreGaps,
           boolean showSequenceLogo, long nseq)
   {
     // long now = System.currentTimeMillis();
@@ -249,7 +252,7 @@ public class AAFrequency
 
     for (int i = iStart; i < width; i++)
     {
-      Profile profile;
+      ProfileI profile;
       if (i >= profiles.length || ((profile = profiles[i]) == null))
       {
         /*
@@ -299,7 +302,7 @@ public class AAFrequency
    *          the number of decimal places to format percentages to
    * @return
    */
-  static String getTooltip(Profile profile, float pid,
+  static String getTooltip(ProfileI profile, float pid,
           boolean showSequenceLogo, boolean ignoreGaps, int dp)
   {
     ResidueCount counts = profile.getCounts();
@@ -350,7 +353,7 @@ public class AAFrequency
    *          calculations
    * @return
    */
-  public static int[] extractProfile(Profile profile,
+  public static int[] extractProfile(ProfileI profile,
           boolean ignoreGaps)
   {
     int[] rtnval = new int[64];
index 8127747..7b9da46 100755 (executable)
@@ -22,25 +22,33 @@ package jalview.analysis;
 
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.ResidueCount;
+import jalview.datamodel.ResidueCount.SymbolCounts;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
-import jalview.ext.android.SparseIntArray;
 import jalview.schemes.ResidueProperties;
+import jalview.util.Comparison;
 
 import java.awt.Color;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.TreeMap;
 import java.util.Vector;
 
 /**
  * Calculates conservation values for a given set of sequences
- * 
- * @author $author$
- * @version $Revision$
  */
 public class Conservation
 {
+  /*
+   * need to have a minimum of 3% of sequences with a residue
+   * for it to be included in the conservation calculation
+   */
+  private static final int THRESHOLD_PERCENT = 3;
+
+  private static final int TOUPPERCASE = 'a' - 'A';
+
   SequenceI[] sequences;
 
   int start;
@@ -54,6 +62,11 @@ public class Conservation
 
   boolean seqNumsChanged = false; // updated after any change via calcSeqNum;
 
+  /*
+   * a map per column with {property, conservation} where conservation value is
+   * 1 (property is conserved), 0 (absence of property is conserved) or -1
+   * (property is not conserved i.e. column has residues with and without it)
+   */
   Map<String, Integer>[] total;
 
   boolean canonicaliseAa = true; // if true then conservation calculation will
@@ -70,6 +83,9 @@ public class Conservation
 
   private Sequence consSequence;
 
+  /*
+   * percentage of residues in a column to qualify for counting conservation
+   */
   private int threshold;
 
   private String name = "";
@@ -79,12 +95,10 @@ public class Conservation
   private String[] consSymbs;
 
   /**
-   * Creates a new Conservation object.
+   * Constructor using default threshold of 3%
    * 
    * @param name
    *          Name of conservation
-   * @param threshold
-   *          to count the residues in residueHash(). commonly used value is 3
    * @param sequences
    *          sequences to be used in calculation
    * @param start
@@ -92,6 +106,27 @@ public class Conservation
    * @param end
    *          end residue position
    */
+  public Conservation(String name, List<SequenceI> sequences, int start,
+          int end)
+  {
+    this(name, THRESHOLD_PERCENT, sequences, start, end);
+  }
+
+  /**
+   * Constructor
+   * 
+   * @param name
+   *          Name of conservation
+   * @param threshold
+   *          percentage of sequences at or below which property conservation is
+   *          ignored
+   * @param sequences
+   *          sequences to be used in calculation
+   * @param start
+   *          start column position
+   * @param end
+   *          end column position
+   */
   public Conservation(String name, int threshold,
           List<SequenceI> sequences, int start, int end)
   {
@@ -189,154 +224,187 @@ public class Conservation
    */
   public void calculate()
   {
-    int jSize = sequences.length;
-    // int[] values; // Replaces residueHash
-    SparseIntArray values = new SparseIntArray();
+    int height = sequences.length;
 
     total = new Map[maxLength];
 
-    for (int i = start; i <= end; i++)
+    for (int column = start; column <= end; column++)
+    {
+      ResidueCount values = countResidues(column);
+
+      /*
+       * percentage count at or below which we ignore residues
+       */
+      int thresh = (threshold * height) / 100;
+
+      /*
+       * check observed residues in column and record whether each 
+       * physico-chemical property is conserved (+1), absence conserved (0),
+       * or not conserved (-1)
+       * Using TreeMap means properties are displayed in alphabetical order
+       */
+      Map<String, Integer> resultHash = new TreeMap<String, Integer>();
+      SymbolCounts symbolCounts = values.getSymbolCounts();
+      char[] symbols = symbolCounts.symbols;
+      int[] counts = symbolCounts.values;
+      for (int j = 0; j < symbols.length; j++)
+      {
+        char c = symbols[j];
+        if (counts[j] > thresh)
+        {
+          recordConservation(resultHash, String.valueOf(c));
+        }
+      }
+      if (values.getGapCount() > thresh)
+      {
+        recordConservation(resultHash, "-");
+      }
+
+      if (total.length > 0)
+      {
+        total[column - start] = resultHash;
+      }
+    }
+  }
+
+  /**
+   * Updates the conservation results for an observed residue
+   * 
+   * @param resultMap
+   *          a map of {property, conservation} where conservation value is +1
+   *          (all residues have the property), 0 (no residue has the property)
+   *          or -1 (some do, some don't)
+   * @param res
+   */
+  protected static void recordConservation(Map<String, Integer> resultMap,
+          String res)
+  {
+    res = res.toUpperCase();
+    for (Entry<String, Map<String, Integer>> property : ResidueProperties.propHash
+            .entrySet())
     {
-      // values = new int[255];
-      values.clear();
+      String propertyName = property.getKey();
+      Integer residuePropertyValue = property.getValue().get(res);
 
-      for (int j = 0; j < jSize; j++)
+      if (!resultMap.containsKey(propertyName))
       {
-        if (sequences[j].getLength() > i)
+        /*
+         * first time we've seen this residue - note whether it has this property
+         */
+        if (residuePropertyValue != null)
         {
-          char c = sequences[j].getCharAt(i);
-
-          if (canonicaliseAa)
-          { // lookup the base aa code symbol
-            c = (char) ResidueProperties.aaIndex[sequences[j].getCharAt(i)];
-            if (c > 20)
-            {
-              c = '-';
-            }
-            else
-            {
-              // recover canonical aa symbol
-              c = ResidueProperties.aa[c].charAt(0);
-            }
-          }
-          else
-          {
-            // original behaviour - operate on ascii symbols directly
-            // No need to check if its a '-'
-            if (c == '.' || c == ' ')
-            {
-              c = '-';
-            }
-
-            c = toUpperCase(c);
-          }
-          // values[c]++;
-          values.add(c, 1);
+          resultMap.put(propertyName, residuePropertyValue);
         }
         else
         {
-          // values['-']++;
-          values.add('-', 1);
+          /*
+           * unrecognised residue - use default value for property
+           */
+          resultMap.put(propertyName, property.getValue().get("-"));
         }
       }
+      else
+      {
+        Integer currentResult = resultMap.get(propertyName);
+        if (currentResult.intValue() != -1
+                && !currentResult.equals(residuePropertyValue))
+        {
+          /*
+           * property is unconserved - residues seen both with and without it
+           */
+          resultMap.put(propertyName, Integer.valueOf(-1));
+        }
+      }
+    }
+  }
 
-      // What is the count threshold to count the residues in residueHash()
-      int thresh = (threshold * jSize) / 100;
+  /**
+   * Counts residues (upper-cased) and gaps in the given column
+   * 
+   * @param column
+   * @return
+   */
+  protected ResidueCount countResidues(int column)
+  {
+    ResidueCount values = new ResidueCount(false);
 
-      // loop over all the found residues
-      // Hashtable<String, Integer> resultHash = new Hashtable<String,
-      // Integer>();
-      Map<String, Integer> resultHash = new TreeMap<String, Integer>();
-      // for (char v = '-'; v < 'Z'; v++)
-      for (int key = 0; key < values.size(); key++)
+    for (int row = 0; row < sequences.length; row++)
+    {
+      if (sequences[row].getLength() > column)
       {
-        char v = (char) values.keyAt(key);
-        // if (values[v] > thresh)
-        if (values.valueAt(key) > thresh)
+        char c = sequences[row].getCharAt(column);
+        if (canonicaliseAa)
         {
-          String res = String.valueOf(v);
-
-          // Now loop over the properties
-          for (String type : ResidueProperties.propHash.keySet())
-          {
-            Map<String, Integer> ht = ResidueProperties.propHash.get(type);
-
-            // Have we ticked this before?
-            if (!resultHash.containsKey(type))
-            {
-              if (ht.containsKey(res))
-              {
-                resultHash.put(type, ht.get(res));
-              }
-              else
-              {
-                resultHash.put(type, ht.get("-"));
-              }
-            }
-            else if (!resultHash.get(type).equals(ht.get(res)))
-            {
-              resultHash.put(type, new Integer(-1));
-            }
-          }
+          int index = ResidueProperties.aaIndex[c];
+          c = index > 20 ? '-' : ResidueProperties.aa[index].charAt(0);
+        }
+        else
+        {
+          c = toUpperCase(c);
+        }
+        if (Comparison.isGap(c))
+        {
+          values.addGap();
+        }
+        else
+        {
+          values.add(c);
         }
       }
-
-      if (total.length > 0)
+      else
       {
-        total[i - start] = resultHash;
+        values.addGap();
       }
     }
+    return values;
   }
 
-  /*****************************************************************************
-   * count conservation for the j'th column of the alignment
+  /**
+   * Counts conservation and gaps for a column of the alignment
    * 
-   * @return { gap count, conserved residue count}
+   * @return { 1 if fully conserved, else 0, gap count }
    */
-  public int[] countConsNGaps(int j)
+  public int[] countConservationAndGaps(int column)
   {
-    int count = 0;
-    int cons = 0;
-    int nres = 0;
-    int[] r = new int[2];
-    char f = '$';
-    int i, iSize = sequences.length;
-    char c;
+    int gapCount = 0;
+    boolean fullyConserved = true;
+    int iSize = sequences.length;
 
-    for (i = 0; i < iSize; i++)
+    if (iSize == 0)
+    {
+      return new int[] { 0, 0 };
+    }
+
+    char lastRes = '0';
+    for (int i = 0; i < iSize; i++)
     {
-      if (j >= sequences[i].getLength())
+      if (column >= sequences[i].getLength())
       {
-        count++;
+        gapCount++;
         continue;
       }
 
-      c = sequences[i].getCharAt(j); // gaps do not have upper/lower case
+      char c = sequences[i].getCharAt(column); // gaps do not have upper/lower case
 
-      if (jalview.util.Comparison.isGap((c)))
+      if (Comparison.isGap((c)))
       {
-        count++;
+        gapCount++;
       }
       else
       {
         c = toUpperCase(c);
-        nres++;
-
-        if (nres == 1)
+        if (lastRes == '0')
         {
-          f = c;
-          cons++;
+          lastRes = c;
         }
-        else if (f == c)
+        if (c != lastRes)
         {
-          cons++;
+          fullyConserved = false;
         }
       }
     }
 
-    r[0] = (nres == cons) ? 1 : 0;
-    r[1] = count;
-
+    int[] r = new int[] { fullyConserved ? 1 : 0, gapCount };
     return r;
   }
 
@@ -351,7 +419,7 @@ public class Conservation
   {
     if ('a' <= c && c <= 'z')
     {
-      c -= (32); // 32 = 'a' - 'A'
+      c -= TOUPPERCASE;
     }
     return c;
   }
@@ -359,14 +427,17 @@ public class Conservation
   /**
    * Calculates the conservation sequence
    * 
-   * @param consflag
-   *          if true, positive conservation; false calculates negative
-   *          conservation
-   * @param percentageGaps
-   *          commonly used value is 25
+   * @param positiveOnly
+   *          if true, calculate positive conservation; else calculate both
+   *          positive and negative conservation
+   * @param maxPercentageGaps
+   *          the percentage of gaps in a column, at or above which no
+   *          conservation is asserted
    */
-  public void verdict(boolean consflag, float percentageGaps)
+  public void verdict(boolean positiveOnly, float maxPercentageGaps)
   {
+    // TODO call this at the end of calculate(), should not be a public method
+
     StringBuilder consString = new StringBuilder(end);
 
     // NOTE THIS SHOULD CHECK IF THE CONSEQUENCE ALREADY
@@ -379,56 +450,43 @@ public class Conservation
     consSymbs = new String[end - start + 1];
     for (int i = start; i <= end; i++)
     {
-      int[] gapcons = countConsNGaps(i);
+      int[] gapcons = countConservationAndGaps(i);
+      boolean fullyConserved = gapcons[0] == 1;
       int totGaps = gapcons[1];
-      float pgaps = ((float) totGaps * 100) / sequences.length;
-      StringBuilder positives = new StringBuilder(64);
-      StringBuilder negatives = new StringBuilder(32);
-      // consSymbs[i - start] = "";
+      float pgaps = (totGaps * 100f) / sequences.length;
 
-      if (percentageGaps > pgaps)
+      if (maxPercentageGaps > pgaps)
       {
         Map<String, Integer> resultHash = total[i - start];
-        // Now find the verdict
         int count = 0;
+        StringBuilder positives = new StringBuilder(64);
+        StringBuilder negatives = new StringBuilder(32);
         for (String type : resultHash.keySet())
         {
           int result = resultHash.get(type).intValue();
-          // Do we want to count +ve conservation or +ve and -ve cons.?
-          if (consflag)
+          if (result == -1)
           {
-            if (result == 1)
-            {
-              // consSymbs[i - start] = type + " " + consSymbs[i - start];
-              positives.append(positives.length() == 0 ? "" : " ");
-              positives.append(type);
-              count++;
-            }
+            /*
+             * not conserved (present or absent)
+             */
+            continue;
           }
-          else
+          count++;
+          if (result == 1)
           {
-            if (result != -1)
-            {
-              if (result == 0)
-              {
-                /*
-                 * add negatively conserved properties on the end
-                 */
-                // consSymbs[i - start] = consSymbs[i - start] + " !" + type;
-                negatives.append(negatives.length() == 0 ? "" : " ");
-                negatives.append("!").append(type);
-              }
-              else
-              {
-                /*
-                 * put positively conserved properties on the front
-                 */
-                // consSymbs[i - start] = type + " " + consSymbs[i - start];
-                positives.append(positives.length() == 0 ? "" : " ");
-                positives.append(type);
-              }
-              count++;
-            }
+            /*
+             * positively conserved property (all residues have it)
+             */
+            positives.append(positives.length() == 0 ? "" : " ");
+            positives.append(type);
+          }
+          if (result == 0 && !positiveOnly)
+          {
+            /*
+             * absense of property is conserved (all residues lack it)
+             */
+            negatives.append(negatives.length() == 0 ? "" : " ");
+            negatives.append("!").append(type);
           }
         }
         if (negatives.length() > 0)
@@ -443,7 +501,7 @@ public class Conservation
         }
         else
         {
-          consString.append((gapcons[0] == 1) ? "*" : "+");
+          consString.append(fullyConserved ? "*" : "+");
         }
       }
       else
@@ -747,29 +805,27 @@ public class Conservation
    * 
    * @param name
    *          - name of conservation
-   * @param threshold
-   *          - minimum number of conserved residues needed to indicate
-   *          conservation (typically 3)
    * @param seqs
    * @param start
    *          first column in calculation window
    * @param end
    *          last column in calculation window
-   * @param posOrNeg
-   *          positive (true) or negative (false) conservation
-   * @param consPercGaps
+   * @param positiveOnly
+   *          calculate positive (true) or positive and negative (false)
+   *          conservation
+   * @param maxPercentGaps
    *          percentage of gaps tolerated in column
    * @param calcQuality
    *          flag indicating if alignment quality should be calculated
    * @return Conservation object ready for use in visualization
    */
   public static Conservation calculateConservation(String name,
-          int threshold, List<SequenceI> seqs, int start, int end,
-          boolean posOrNeg, int consPercGaps, boolean calcQuality)
+          List<SequenceI> seqs, int start, int end, boolean positiveOnly,
+          int maxPercentGaps, boolean calcQuality)
   {
-    Conservation cons = new Conservation(name, threshold, seqs, start, end);
+    Conservation cons = new Conservation(name, seqs, start, end);
     cons.calculate();
-    cons.verdict(posOrNeg, consPercGaps);
+    cons.verdict(positiveOnly, maxPercentGaps);
 
     if (calcQuality)
     {
@@ -778,4 +834,24 @@ public class Conservation
 
     return cons;
   }
+
+  /**
+   * Returns the computed tooltip (annotation description) for a given column.
+   * The tip is empty if the conservation score is zero, otherwise holds the
+   * conserved properties (and, optionally, properties whose absence is
+   * conserved).
+   * 
+   * @param column
+   * @return
+   */
+  String getTooltip(int column)
+  {
+    char[] sequence = getConsSequence().getSequence();
+    char val = column < sequence.length ? sequence[column] : '-';
+    boolean hasConservation = val != '-' && val != '0';
+    int consp = column - start;
+    String tip = (hasConservation && consp > -1 && consp < consSymbs.length) ? consSymbs[consp]
+            : "";
+    return tip;
+  }
 }
index 70463e7..e30a052 100644 (file)
 package jalview.api;
 
 import jalview.analysis.Conservation;
-import jalview.analysis.Profile;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.ProfileI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
@@ -82,7 +82,7 @@ public interface AlignViewportI extends ViewStyleI
 
   ColumnSelection getColumnSelection();
 
-  Profile[] getSequenceConsensusHash();
+  ProfileI[] getSequenceConsensusHash();
 
   /**
    * Get consensus data table for the cDNA complement of this alignment (if any)
@@ -145,7 +145,7 @@ public interface AlignViewportI extends ViewStyleI
    * 
    * @param hconsensus
    */
-  void setSequenceConsensusHash(Profile[] hconsensus);
+  void setSequenceConsensusHash(ProfileI[] hconsensus);
 
   /**
    * Set the cDNA complement consensus for the viewport
index c30fdad..d625f78 100644 (file)
@@ -24,12 +24,12 @@ import jalview.analysis.AAFrequency;
 import jalview.analysis.AlignmentAnnotationUtils;
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.Conservation;
+import jalview.bin.Cache;
 import jalview.commands.ChangeCaseCommand;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
@@ -59,6 +59,7 @@ import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -253,122 +254,9 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
     if (links != null && links.size() > 0)
     {
-      Menu linkMenu = new Menu(MessageManager.getString("action.link"));
-      for (int i = 0; i < links.size(); i++)
-      {
-        String link = links.elementAt(i);
-        UrlLink urlLink = new UrlLink(link);
-        if (!urlLink.isValid())
-        {
-          System.err.println(urlLink.getInvalidMessage());
-          continue;
-        }
-        final String target = urlLink.getTarget(); // link.substring(0,
-        // link.indexOf("|"));
-        final String label = urlLink.getLabel();
-        if (seq != null && urlLink.isDynamic())
-        {
-
-          // collect matching db-refs
-          DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(
-                  seq.getDBRefs(), new String[] { target });
-          // collect id string too
-          String id = seq.getName();
-          String descr = seq.getDescription();
-          if (descr != null && descr.length() < 1)
-          {
-            descr = null;
-          }
-          if (dbr != null)
-          {
-            for (int r = 0; r < dbr.length; r++)
-            {
-              if (id != null && dbr[r].getAccessionId().equals(id))
-              {
-                // suppress duplicate link creation for the bare sequence ID
-                // string with this link
-                id = null;
-              }
-              // create Bare ID link for this RUL
-              String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(),
-                      true);
-              if (urls != null)
-              {
-                for (int u = 0; u < urls.length; u += 2)
-                {
-                  addshowLink(linkMenu, label + "|" + urls[u], urls[u + 1]);
-                }
-              }
-            }
-          }
-          if (id != null)
-          {
-            // create Bare ID link for this RUL
-            String[] urls = urlLink.makeUrls(id, true);
-            if (urls != null)
-            {
-              for (int u = 0; u < urls.length; u += 2)
-              {
-                addshowLink(linkMenu, label, urls[u + 1]);
-              }
-            }
-            // addshowLink(linkMenu, target, url_pref + id + url_suff);
-          }
-          // Now construct URLs from description but only try to do it for regex
-          // URL links
-          if (descr != null && urlLink.getRegexReplace() != null)
-          {
-            // create link for this URL from description only if regex matches
-            String[] urls = urlLink.makeUrls(descr, true);
-            if (urls != null)
-            {
-              for (int u = 0; u < urls.length; u += 2)
-              {
-                addshowLink(linkMenu, label, urls[u + 1]);
-              }
-            }
-          }
-        }
-        else
-        {
-          addshowLink(linkMenu, target, urlLink.getUrl_prefix()); // link.substring(link.lastIndexOf("|")+1));
-        }
-        /*
-         * final String url;
-         * 
-         * if (link.indexOf("$SEQUENCE_ID$") > -1) { // Substitute SEQUENCE_ID
-         * string and any matching database reference accessions String url_pref
-         * = link.substring(link.indexOf("|") + 1,
-         * link.indexOf("$SEQUENCE_ID$"));
-         * 
-         * String url_suff = link.substring(link.indexOf("$SEQUENCE_ID$") + 13);
-         * // collect matching db-refs DBRefEntry[] dbr =
-         * jalview.util.DBRefUtils.selectRefs(seq.getDBRef(), new
-         * String[]{target}); // collect id string too String id =
-         * seq.getName(); if (id.indexOf("|") > -1) { id =
-         * id.substring(id.lastIndexOf("|") + 1); } if (dbr!=null) { for (int
-         * r=0;r<dbr.length; r++) { if (dbr[r].getAccessionId().equals(id)) { //
-         * suppress duplicate link creation for the bare sequence ID string with
-         * this link id = null; } addshowLink(linkMenu,
-         * dbr[r].getSource()+"|"+dbr[r].getAccessionId(), target,
-         * url_pref+dbr[r].getAccessionId()+url_suff); } } if (id!=null) { //
-         * create Bare ID link for this RUL addshowLink(linkMenu, target,
-         * url_pref + id + url_suff); } } else { addshowLink(linkMenu, target,
-         * link.substring(link.lastIndexOf("|")+1)); }
-         */
-      }
-      if (linkMenu.getItemCount() > 0)
-      {
-        if (seq != null)
-        {
-          seqMenu.add(linkMenu);
-        }
-        else
-        {
-          add(linkMenu);
-        }
-      }
+      addFeatureLinks(seq, links);
     }
+
     // TODO: add group link menu entry here
     if (seq != null)
     {
@@ -414,6 +302,62 @@ public class APopupMenu extends java.awt.PopupMenu implements
   }
 
   /**
+   * Adds a 'Link' menu item with a sub-menu item for each hyperlink provided.
+   * 
+   * @param seq
+   * @param links
+   */
+  void addFeatureLinks(final SequenceI seq, List<String> links)
+  {
+    Menu linkMenu = new Menu(MessageManager.getString("action.link"));
+    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+
+    for (String link : links)
+    {
+      UrlLink urlLink = null;
+      try
+      {
+        urlLink = new UrlLink(link);
+      } catch (Exception foo)
+      {
+        Cache.log.error("Exception for URLLink '" + link + "'", foo);
+        continue;
+      }
+
+      if (!urlLink.isValid())
+      {
+        Cache.log.error(urlLink.getInvalidMessage());
+        continue;
+      }
+
+      urlLink.createLinksFromSeq(seq, linkset);
+    }
+
+    addshowLinks(linkMenu, linkset.values());
+
+    if (linkMenu.getItemCount() > 0)
+    {
+      if (seq != null)
+      {
+        seqMenu.add(linkMenu);
+      }
+      else
+      {
+        add(linkMenu);
+      }
+    }
+  }
+
+  private void addshowLinks(Menu linkMenu, Collection<List<String>> linkset)
+  {
+    for (List<String> linkstrset : linkset)
+    {
+      // split linkstr into label and url
+      addshowLink(linkMenu, linkstrset.get(1), linkstrset.get(3));
+    }
+  }
+
+  /**
    * Build menus for annotation types that may be shown or hidden, and for
    * 'reference annotations' that may be added to the alignment.
    */
@@ -1206,11 +1150,10 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
     if (conservationMenuItem.getState())
     {
-
-      sg.cs.setConservation(Conservation.calculateConservation("Group", 3,
-              sg.getSequences(ap.av.getHiddenRepSequences()), 0, ap.av
-                      .getAlignment().getWidth(), false, ap.av
-                      .getConsPercGaps(), false));
+      sg.cs.setConservation(Conservation.calculateConservation("Group", sg
+              .getSequences(ap.av.getHiddenRepSequences()), 0, ap.av
+              .getAlignment().getWidth(), false, ap.av.getConsPercGaps(),
+              false));
       SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
       SliderPanel.showConservationSlider();
     }
index 2cb3060..182f20e 100755 (executable)
@@ -246,7 +246,14 @@ public class IdPanel extends Panel implements MouseListener,
         url = null;
         continue;
       }
-      ;
+
+      if (urlLink.usesDBAccession())
+      {
+        // this URL requires an accession id, not the name of a sequence
+        url = null;
+        continue;
+      }
+
       if (!urlLink.isValid())
       {
         System.err.println(urlLink.getInvalidMessage());
index 3b509e5..8292a5a 100755 (executable)
@@ -676,8 +676,8 @@ public class TreeCanvas extends Panel implements MouseListener,
       if (av.getGlobalColourScheme() != null
               && av.getGlobalColourScheme().conservationApplied())
       {
-        Conservation c = new Conservation("Group", 3,
-                sg.getSequences(null), sg.getStartRes(), sg.getEndRes());
+        Conservation c = new Conservation("Group", sg.getSequences(null),
+                sg.getStartRes(), sg.getEndRes());
 
         c.calculate();
         c.verdict(false, av.getConsPercGaps());
similarity index 59%
rename from src/jalview/analysis/Profile.java
rename to src/jalview/datamodel/Profile.java
index d94d031..5464596 100644 (file)
@@ -1,22 +1,21 @@
-package jalview.analysis;
+package jalview.datamodel;
 
 
 /**
- * A data bean to hold the result of computing a profile for a column of an
- * alignment
+ * A profile for one column of an alignment
  * 
  * @author gmcarstairs
  *
  */
-public class Profile
+public class Profile implements ProfileI
 {
   /*
-   * counts of keys (chars)
+   * an object holding counts of symbols in the profile
    */
   private ResidueCount counts;
 
   /*
-   * the number of sequences in the profile
+   * the number of sequences (gapped or not) in the profile
    */
   private int height;
 
@@ -57,24 +56,19 @@ public class Profile
     this.modalResidue = modalRes;
   }
 
-  /**
-   * Set the full profile of counts
-   * 
-   * @param residueCounts
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#setCounts(jalview.datamodel.ResidueCount)
    */
+  @Override
   public void setCounts(ResidueCount residueCounts)
   {
     this.counts = residueCounts;
   }
 
-  /**
-   * Returns the percentage identity of the profile, i.e. the highest proportion
-   * of conserved (equal) symbols. The percentage is as a fraction of all
-   * sequences, or only ungapped sequences if flag ignoreGaps is set true.
-   * 
-   * @param ignoreGaps
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getPercentageIdentity(boolean)
    */
+  @Override
   public float getPercentageIdentity(boolean ignoreGaps)
   {
     if (height == 0)
@@ -93,63 +87,55 @@ public class Profile
     return pid;
   }
 
-  /**
-   * Returns the full symbol counts for this profile
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getCounts()
    */
+  @Override
   public ResidueCount getCounts()
   {
     return counts;
   }
 
-  /**
-   * Returns the number of sequences in the profile
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getHeight()
    */
+  @Override
   public int getHeight()
   {
     return height;
   }
 
-  /**
-   * Returns the number of sequences in the profile which had a gap character
-   * (or were too short to be included in this column's profile)
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getGapped()
    */
+  @Override
   public int getGapped()
   {
     return gapped;
   }
 
-  /**
-   * Returns the highest count for any symbol(s) in the profile
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getMaxCount()
    */
+  @Override
   public int getMaxCount()
   {
     return maxCount;
   }
 
-  /**
-   * Returns the symbol (or concatenated symbols) which have the highest count
-   * in the profile, or an empty string if there were no symbols counted
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getModalResidue()
    */
+  @Override
   public String getModalResidue()
   {
     return modalResidue;
   }
 
-  /**
-   * Answers the number of non-gapped sequences in the profile
-   * 
-   * @return
+  /* (non-Javadoc)
+   * @see jalview.datamodel.ProfileI#getNonGapped()
    */
+  @Override
   public int getNonGapped()
   {
     return height - gapped;
diff --git a/src/jalview/datamodel/ProfileI.java b/src/jalview/datamodel/ProfileI.java
new file mode 100644 (file)
index 0000000..cf2b394
--- /dev/null
@@ -0,0 +1,67 @@
+package jalview.datamodel;
+
+public interface ProfileI
+{
+
+  /**
+   * Set the full profile of counts
+   * 
+   * @param residueCounts
+   */
+  public abstract void setCounts(ResidueCount residueCounts);
+
+  /**
+   * Returns the percentage identity of the profile, i.e. the highest proportion
+   * of conserved (equal) symbols. The percentage is as a fraction of all
+   * sequences, or only ungapped sequences if flag ignoreGaps is set true.
+   * 
+   * @param ignoreGaps
+   * @return
+   */
+  public abstract float getPercentageIdentity(boolean ignoreGaps);
+
+  /**
+   * Returns the full symbol counts for this profile
+   * 
+   * @return
+   */
+  public abstract ResidueCount getCounts();
+
+  /**
+   * Returns the number of sequences in the profile
+   * 
+   * @return
+   */
+  public abstract int getHeight();
+
+  /**
+   * Returns the number of sequences in the profile which had a gap character
+   * (or were too short to be included in this column's profile)
+   * 
+   * @return
+   */
+  public abstract int getGapped();
+
+  /**
+   * Returns the highest count for any symbol(s) in the profile
+   * 
+   * @return
+   */
+  public abstract int getMaxCount();
+
+  /**
+   * Returns the symbol (or concatenated symbols) which have the highest count
+   * in the profile, or an empty string if there were no symbols counted
+   * 
+   * @return
+   */
+  public abstract String getModalResidue();
+
+  /**
+   * Answers the number of non-gapped sequences in the profile
+   * 
+   * @return
+   */
+  public abstract int getNonGapped();
+
+}
\ No newline at end of file
similarity index 99%
rename from src/jalview/analysis/ResidueCount.java
rename to src/jalview/datamodel/ResidueCount.java
index 75decf2..0d0348c 100644 (file)
@@ -1,4 +1,4 @@
-package jalview.analysis;
+package jalview.datamodel;
 
 import jalview.util.Comparison;
 import jalview.util.Format;
@@ -95,7 +95,7 @@ public class ResidueCount
 
   /*
    * keeps track of the maximum count value recorded
-   * (if this class every allows decrements, would need to
+   * (if this class ever allows decrements, would need to
    * calculate this on request instead) 
    */
   int maxCount;
index 98fd8f2..ca90003 100755 (executable)
@@ -22,7 +22,6 @@ package jalview.datamodel;
 
 import jalview.analysis.AAFrequency;
 import jalview.analysis.Conservation;
-import jalview.analysis.Profile;
 import jalview.schemes.ColourSchemeI;
 
 import java.awt.Color;
@@ -528,7 +527,7 @@ public class SequenceGroup implements AnnotatedCollectionI
     boolean upd = false;
     try
     {
-      Profile[] cnsns = AAFrequency.calculate(sequences, startRes,
+      ProfileI[] cnsns = AAFrequency.calculate(sequences, startRes,
               endRes + 1, showSequenceLogo);
       if (consensus != null)
       {
@@ -544,8 +543,8 @@ public class SequenceGroup implements AnnotatedCollectionI
       if ((conservation != null)
               || (cs != null && cs.conservationApplied()))
       {
-        Conservation c = new Conservation(groupName, 3, sequences,
-                startRes, endRes + 1);
+        Conservation c = new Conservation(groupName, sequences, startRes,
+                endRes + 1);
         c.calculate();
         c.verdict(false, consPercGaps);
         if (conservation != null)
@@ -600,9 +599,9 @@ public class SequenceGroup implements AnnotatedCollectionI
     c.completeAnnotations(conservation, null, startRes, endRes + 1);
   }
 
-  public Profile[] consensusData = null;
+  public ProfileI[] consensusData = null;
 
-  private void _updateConsensusRow(Profile[] cnsns, long nseq)
+  private void _updateConsensusRow(ProfileI[] cnsns, long nseq)
   {
     if (consensus == null)
     {
index 3f457ea..b1b913b 100644 (file)
@@ -82,9 +82,12 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.Semaphore;
 
 import javax.swing.AbstractAction;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
 import javax.swing.DefaultDesktopManager;
 import javax.swing.DesktopManager;
 import javax.swing.JButton;
+import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JComponent;
 import javax.swing.JDesktopPane;
@@ -382,6 +385,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
     showNews.setVisible(false);
 
+    checkURLLinks();
+
     this.addWindowListener(new WindowAdapter()
     {
       @Override
@@ -2256,6 +2261,55 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     new Thread(jvq).start();
   }
 
+  public void checkURLLinks()
+  {
+    // Thread off the URL link checker
+    addDialogThread(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        if (Cache.getDefault("CHECKURLLINKS", true))
+        {
+          // check in case URL links use old style tokens ($SEQUENCE_ID$ for
+          // sequence id _or_ accession id)
+          JPanel msgPanel = new JPanel();
+          msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
+          msgPanel.add(Box.createVerticalGlue());
+          JLabel msg = new JLabel(
+                  MessageManager
+                          .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
+          JLabel msg2 = new JLabel(
+                  MessageManager
+                          .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
+          msgPanel.add(msg);
+          msgPanel.add(msg2);
+
+          final JCheckBox jcb = new JCheckBox(
+                  MessageManager.getString("label.do_not_display_again"));
+          jcb.addActionListener(new ActionListener()
+          {
+
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+              // update Cache settings if checkbox is selected
+              if (jcb.isSelected())
+              {
+                Cache.setProperty("CHECKURLLINKS", "false");
+              }
+            }
+          });
+          msgPanel.add(jcb);
+
+          JOptionPane.showMessageDialog(null, msgPanel, MessageManager
+                  .getString("label.SEQUENCE_ID_no_longer_used"),
+                  JOptionPane.WARNING_MESSAGE);
+        }
+      }
+    });
+    }
+
   /**
    * Proxy class for JDesktopPane which optionally displays the current memory
    * usage and highlights the desktop area with a red bar if free memory runs
index a65be7b..e321a74 100755 (executable)
@@ -225,7 +225,14 @@ public class IdPanel extends JPanel implements MouseListener,
         url = null;
         continue;
       }
-      ;
+
+      if (urlLink.usesDBAccession())
+      {
+        // this URL requires an accession id, not the name of a sequence
+        url = null;
+        continue;
+      }
+
       if (!urlLink.isValid())
       {
         jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
index 1c90889..bc5e1d8 100644 (file)
@@ -3401,8 +3401,8 @@ public class Jalview2XML
         }
         if (jGroup.getConsThreshold() != 0)
         {
-          Conservation c = new Conservation("All", 3,
-                  sg.getSequences(null), 0, sg.getWidth() - 1);
+          Conservation c = new Conservation("All", sg.getSequences(null),
+                  0, sg.getWidth() - 1);
           c.calculate();
           c.verdict(false, 25);
           sg.cs.setConservation(c);
index f8a296f..3b39be7 100755 (executable)
@@ -359,8 +359,8 @@ public class Jalview2XML_V1
 
         if (groups[i].getConsThreshold() != 0)
         {
-          Conservation c = new Conservation("All", 3,
-                  sg.getSequences(null), 0, sg.getWidth() - 1);
+          Conservation c = new Conservation("All", sg.getSequences(null),
+                  0, sg.getWidth() - 1);
           c.calculate();
           c.verdict(false, 25);
           sg.cs.setConservation(c);
index e2d397f..b342f86 100644 (file)
@@ -54,7 +54,6 @@ import jalview.schemes.TaylorColourScheme;
 import jalview.schemes.TurnColourScheme;
 import jalview.schemes.UserColourScheme;
 import jalview.schemes.ZappoColourScheme;
-import jalview.util.DBRefUtils;
 import jalview.util.GroupUrlLink;
 import jalview.util.GroupUrlLink.UrlStringTooLongException;
 import jalview.util.MessageManager;
@@ -63,8 +62,8 @@ import jalview.util.UrlLink;
 import java.awt.Color;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Hashtable;
 import java.util.LinkedHashMap;
@@ -617,7 +616,8 @@ public class PopupMenu extends JPopupMenu
   void addFeatureLinks(final SequenceI seq, List<String> links)
   {
     JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
-    List<String> linkset = new ArrayList<String>();
+    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+
     for (String link : links)
     {
       UrlLink urlLink = null;
@@ -629,80 +629,18 @@ public class PopupMenu extends JPopupMenu
         Cache.log.error("Exception for URLLink '" + link + "'", foo);
         continue;
       }
-      ;
+
       if (!urlLink.isValid())
       {
         Cache.log.error(urlLink.getInvalidMessage());
         continue;
       }
-      final String label = urlLink.getLabel();
-
-      // collect id string too
-      String id = seq.getName();
-      String descr = seq.getDescription();
-      if (descr != null && descr.length() < 1)
-      {
-        descr = null;
-      }
-
-      if (seq != null && urlLink.usesSeqId()) // link is ID
-      {
-        // collect matching db-refs
-        DBRefEntry[] dbr = DBRefUtils.selectRefs(seq.getDBRefs(),
-                new String[] { urlLink.getTarget() });
-
-        // if there are any dbrefs which match up with the link
-        if (dbr != null)
-        {
-          for (int r = 0; r < dbr.length; r++)
-          {
-            if (id != null && dbr[r].getAccessionId().equals(id))
-            {
-              // suppress duplicate link creation for the bare sequence ID
-              // string with this link
-              id = null;
-            }
-            // create Bare ID link for this URL
-            createBareURLLink(urlLink, dbr[r].getAccessionId(), linkset,
-                    linkMenu, label, true);
-          }
-        }
 
-        // Create urls from description but only for URL links which are regex
-        // links
-        if (descr != null && urlLink.getRegexReplace() != null)
-        {
-          // create link for this URL from description where regex matches
-          createBareURLLink(urlLink, descr, linkset, linkMenu, label, false);
-        }
+      urlLink.createLinksFromSeq(seq, linkset);
+    }
 
-      }
-      else if (seq != null && !urlLink.usesSeqId()) // link is name
-      {
-        if (id != null)
-        {
-          // create Bare ID link for this URL
-          createBareURLLink(urlLink, id, linkset, linkMenu, label, false);
-        }
-        // Create urls from description but only for URL links which are regex
-        // links
-        if (descr != null && urlLink.getRegexReplace() != null)
-        {
-          // create link for this URL from description where regex matches
-          createBareURLLink(urlLink, descr, linkset, linkMenu, label, false);
-        }
-      }
-      else
-      {
-        if (!linkset.contains(label + "|" + urlLink.getUrl_prefix()))
-        {
-          linkset.add(label + "|" + urlLink.getUrl_prefix());
-          // Add a non-dynamic link
-          addshowLink(linkMenu, label, urlLink.getUrl_prefix());
-        }
-      }
+    addshowLinks(linkMenu, linkset.values());
 
-    }
     if (sequence != null)
     {
       sequenceMenu.add(linkMenu);
@@ -713,33 +651,7 @@ public class PopupMenu extends JPopupMenu
     }
   }
 
-  /*
-   * Create a bare URL Link
-   */
-  private void createBareURLLink(UrlLink urlLink, String id,
-          List<String> linkset, JMenu linkMenu, String label,
-          Boolean addSepToLabel)
-  {
-    String[] urls = urlLink.makeUrls(id, true);
-    if (urls != null)
-    {
-      for (int u = 0; u < urls.length; u += 2)
-      {
-        if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
-        {
-          linkset.add(urls[u] + "|" + urls[u + 1]);
-          if (addSepToLabel)
-          {
-            addshowLink(linkMenu, label + "|" + urls[u], urls[u + 1]);
-          }
-          else
-          {
-            addshowLink(linkMenu, label, urls[u + 1]);
-          }
-        }
-      }
-    }
-  }
+
 
   /**
    * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
@@ -1015,6 +927,15 @@ public class PopupMenu extends JPopupMenu
     }
   }
 
+  private void addshowLinks(JMenu linkMenu, Collection<List<String>> linkset)
+  {
+    for (List<String> linkstrset : linkset)
+    {
+      // split linkstr into label and url
+      addshowLink(linkMenu, linkstrset.get(1), linkstrset.get(3));
+    }
+  }
+
   /**
    * add a show URL menu item to the given linkMenu
    * 
@@ -2048,7 +1969,7 @@ public class PopupMenu extends JPopupMenu
     if (conservationMenuItem.isSelected())
     {
       // JBPNote: Conservation name shouldn't be i18n translated
-      Conservation c = new Conservation("Group", 3, sg.getSequences(ap.av
+      Conservation c = new Conservation("Group", sg.getSequences(ap.av
               .getHiddenRepSequences()), sg.getStartRes(),
               sg.getEndRes() + 1);
 
index b57b951..4f094e1 100755 (executable)
@@ -21,9 +21,8 @@
 package jalview.gui;
 
 import static jalview.util.UrlConstants.EMBLEBI_STRING;
-import static jalview.util.UrlConstants.OLD_EMBLEBI_STRING;
+import static jalview.util.UrlConstants.DB_ACCESSION;
 import static jalview.util.UrlConstants.SEQUENCE_ID;
-import static jalview.util.UrlConstants.SEQUENCE_NAME;
 import static jalview.util.UrlConstants.SRS_STRING;
 
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
@@ -127,10 +126,10 @@ public class Preferences extends GPreferences
         String name = st.nextToken();
         String url = st.nextToken();
         // check for '|' within a regex
-        int rxstart = url.indexOf("$" + SEQUENCE_ID + "$");
+        int rxstart = url.indexOf("$" + DB_ACCESSION + "$");
         if (rxstart == -1)
         {
-          rxstart = url.indexOf("$" + SEQUENCE_NAME + "$");
+          rxstart = url.indexOf("$" + SEQUENCE_ID + "$");
         }
         while (rxstart == -1 && url.indexOf("/=$") == -1)
         {
@@ -149,12 +148,6 @@ public class Preferences extends GPreferences
       {
         sequenceURLLinks.setElementAt(EMBLEBI_STRING, srsPos);
       }
-      // upgrade old EMBL-EBI link
-      int emblPos = sequenceURLLinks.indexOf(OLD_EMBLEBI_STRING);
-      if (emblPos > -1)
-      {
-        sequenceURLLinks.setElementAt(EMBLEBI_STRING, emblPos);
-      }
     }
 
     /**
index 0e513f7..84fd82f 100755 (executable)
@@ -1017,8 +1017,8 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
         if (aps[a].av.getGlobalColourScheme() != null
                 && aps[a].av.getGlobalColourScheme().conservationApplied())
         {
-          Conservation c = new Conservation("Group", 3,
-                  sg.getSequences(null), sg.getStartRes(), sg.getEndRes());
+          Conservation c = new Conservation("Group", sg.getSequences(null),
+                  sg.getStartRes(), sg.getEndRes());
           c.calculate();
           c.verdict(false, aps[a].av.getConsPercGaps());
           sg.cs.setConservation(c);
index 34fdabe..61a30f8 100755 (executable)
@@ -1637,9 +1637,8 @@ public class AnnotationFile
         else if (key.equalsIgnoreCase("consThreshold"))
         {
           sg.cs.setConservationInc(Integer.parseInt(value));
-          Conservation c = new Conservation("Group", 3,
-                  sg.getSequences(null), sg.getStartRes(),
-                  sg.getEndRes() + 1);
+          Conservation c = new Conservation("Group", sg.getSequences(null),
+                  sg.getStartRes(), sg.getEndRes() + 1);
 
           c.calculate();
           c.verdict(false, 25); // TODO: refer to conservation percent threshold
index 07b88bf..89f1068 100644 (file)
@@ -24,10 +24,11 @@ import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.io.gff.GffConstants;
-import jalview.util.DBRefUtils;
 import jalview.util.UrlLink;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -206,16 +207,17 @@ public class SequenceAnnotationReport
         {
           try
           {
-            for (String[] urllink : createLinksFrom(null, urlstring))
+            for (List<String> urllink : createLinksFrom(null, urlstring))
             {
               sb.append("<br/> <a href=\""
-                      + urllink[3]
+                      + urllink.get(3)
                       + "\" target=\""
-                      + urllink[0]
+                      + urllink.get(0)
                       + "\">"
-                      + (urllink[0].toLowerCase().equals(
-                              urllink[1].toLowerCase()) ? urllink[0]
-                              : (urllink[0] + ":" + urllink[1]))
+                      + (urllink.get(0).toLowerCase()
+                              .equals(urllink.get(1).toLowerCase()) ? urllink
+                              .get(0) : (urllink.get(0) + ":" + urllink
+                              .get(1)))
                       + "</a></br>");
             }
           } catch (Exception x)
@@ -234,124 +236,22 @@ public class SequenceAnnotationReport
    * 
    * @param seq
    * @param link
-   * @return String[][] { String[] { link target, link label, dynamic component
-   *         inserted (if any), url }}
+   * @return Collection< List<String> > { List<String> { link target, link
+   *         label, dynamic component inserted (if any), url }}
    */
-  String[][] createLinksFrom(SequenceI seq, String link)
+  Collection<List<String>> createLinksFrom(SequenceI seq, String link)
   {
-    List<String[]> urlSets = new ArrayList<String[]>();
-    List<String> uniques = new ArrayList<String>();
+    Map<String, List<String>> urlSets = new LinkedHashMap<String, List<String>>();
     UrlLink urlLink = new UrlLink(link);
     if (!urlLink.isValid())
     {
       System.err.println(urlLink.getInvalidMessage());
       return null;
     }
-    if (seq != null && urlLink.isDynamic())
-    {
-      urlSets.addAll(createDynamicLinks(seq, urlLink, uniques));
-    }
-    else
-    {
-      String target = urlLink.getTarget();
-      String label = urlLink.getLabel();
-      String unq = label + "|" + urlLink.getUrl_prefix();
-      if (!uniques.contains(unq))
-      {
-        uniques.add(unq);
-        urlSets.add(new String[] { target, label, null,
-            urlLink.getUrl_prefix() });
-      }
-    }
-
-    return urlSets.toArray(new String[][] {});
-  }
 
-  /**
-   * Formats and returns a list of dynamic href links
-   * 
-   * @param seq
-   * @param urlLink
-   * @param uniques
-   */
-  List<String[]> createDynamicLinks(SequenceI seq, UrlLink urlLink,
-          List<String> uniques)
-  {
-    List<String[]> result = new ArrayList<String[]>();
-    final String target = urlLink.getTarget();
-    final String label = urlLink.getLabel();
+    urlLink.createLinksFromSeq(seq, urlSets);
 
-    // collect matching db-refs
-    DBRefEntry[] dbr = DBRefUtils.selectRefs(seq.getDBRefs(),
-            new String[] { target });
-    // collect id string too
-    String id = seq.getName();
-    String descr = seq.getDescription();
-    if (descr != null && descr.length() < 1)
-    {
-      descr = null;
-    }
-    if (dbr != null)
-    {
-      for (int r = 0; r < dbr.length; r++)
-      {
-        if (id != null && dbr[r].getAccessionId().equals(id))
-        {
-          // suppress duplicate link creation for the bare sequence ID
-          // string with this link
-          id = null;
-        }
-        // create Bare ID link for this URL
-        String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(), true);
-        if (urls != null)
-        {
-          for (int u = 0; u < urls.length; u += 2)
-          {
-            String unq = urls[u] + "|" + urls[u + 1];
-            if (!uniques.contains(unq))
-            {
-              result.add(new String[] { target, label, urls[u], urls[u + 1] });
-              uniques.add(unq);
-            }
-          }
-        }
-      }
-    }
-    if (id != null)
-    {
-      // create Bare ID link for this URL
-      String[] urls = urlLink.makeUrls(id, true);
-      if (urls != null)
-      {
-        for (int u = 0; u < urls.length; u += 2)
-        {
-          String unq = urls[u] + "|" + urls[u + 1];
-          if (!uniques.contains(unq))
-          {
-            result.add(new String[] { target, label, urls[u], urls[u + 1] });
-            uniques.add(unq);
-          }
-        }
-      }
-    }
-    if (descr != null && urlLink.getRegexReplace() != null)
-    {
-      // create link for this URL from description only if regex matches
-      String[] urls = urlLink.makeUrls(descr, true);
-      if (urls != null)
-      {
-        for (int u = 0; u < urls.length; u += 2)
-        {
-          String unq = urls[u] + "|" + urls[u + 1];
-          if (!uniques.contains(unq))
-          {
-            result.add(new String[] { target, label, urls[u], urls[u + 1] });
-            uniques.add(unq);
-          }
-        }
-      }
-    }
-    return result;
+    return urlSets.values();
   }
 
   public void createSequenceAnnotationReport(final StringBuffer tip,
index ed758c5..d7ae950 100644 (file)
@@ -22,13 +22,13 @@ package jalview.renderer;
 
 import jalview.analysis.AAFrequency;
 import jalview.analysis.CodingUtils;
-import jalview.analysis.Profile;
 import jalview.analysis.Rna;
 import jalview.analysis.StructureFrequency;
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.ProfileI;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ResidueProperties;
 import jalview.util.Platform;
@@ -74,7 +74,7 @@ public class AnnotationRenderer
 
   private ColumnSelection columnSelection;
 
-  private Profile[] hconsensus;
+  private ProfileI[] hconsensus;
 
   private Hashtable[] complementConsensus;
 
index 165ece9..fb71686 100755 (executable)
@@ -20,8 +20,8 @@
  */
 package jalview.schemes;
 
-import jalview.analysis.Profile;
 import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.ProfileI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
 
@@ -53,7 +53,7 @@ public interface ColourSchemeI
   /**
    * assign the given consensus profile for the colourscheme
    */
-  public void setConsensus(Profile[] hconsensus);
+  public void setConsensus(ProfileI[] hconsensus);
 
   /**
    * assign the given conservation to the colourscheme
index 0dcf960..86fce4e 100644 (file)
@@ -21,7 +21,7 @@
 package jalview.schemes;
 
 import jalview.analysis.Conservation;
-import jalview.analysis.Profile;
+import jalview.datamodel.ProfileI;
 
 /**
  * Colourscheme that takes its colours from some other colourscheme
@@ -40,7 +40,7 @@ public class FollowerColourScheme extends ResidueColourScheme
   }
 
   @Override
-  public void setConsensus(Profile[] consensus)
+  public void setConsensus(ProfileI[] consensus)
   {
     if (colourScheme != null)
     {
index a15ca20..31b8320 100755 (executable)
@@ -21,8 +21,8 @@
 package jalview.schemes;
 
 import jalview.analysis.Conservation;
-import jalview.analysis.Profile;
 import jalview.datamodel.AnnotatedCollectionI;
+import jalview.datamodel.ProfileI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
 import jalview.util.ColorUtils;
@@ -54,7 +54,7 @@ public class ResidueColourScheme implements ColourSchemeI
   /*
    * Consensus data indexed by column
    */
-  Profile[] consensus;
+  ProfileI[] consensus;
 
   /*
    * Conservation string as a char array 
@@ -234,7 +234,7 @@ public class ResidueColourScheme implements ColourSchemeI
    *          DOCUMENT ME!
    */
   @Override
-  public void setConsensus(Profile[] consensus)
+  public void setConsensus(ProfileI[] consensus)
   {
     if (consensus == null)
     {
index 90a7952..f855fde 100755 (executable)
@@ -222,10 +222,14 @@ public class ResidueProperties
     purinepyrimidineIndex['n'] = 2;
   }
 
+  private static final Integer ONE = Integer.valueOf(1);
+
+  private static final Integer ZERO = Integer.valueOf(0);
+
   static
   {
-    aa3Hash.put("ALA", Integer.valueOf(0));
-    aa3Hash.put("ARG", Integer.valueOf(1));
+    aa3Hash.put("ALA", ZERO);
+    aa3Hash.put("ARG", ONE);
     aa3Hash.put("ASN", Integer.valueOf(2));
     aa3Hash.put("ASP", Integer.valueOf(3)); // D
     aa3Hash.put("CYS", Integer.valueOf(4));
@@ -910,267 +914,267 @@ public class ResidueProperties
 
   static
   {
-    hydrophobic.put("I", Integer.valueOf(1));
-    hydrophobic.put("L", Integer.valueOf(1));
-    hydrophobic.put("V", Integer.valueOf(1));
-    hydrophobic.put("C", Integer.valueOf(1));
-    hydrophobic.put("A", Integer.valueOf(1));
-    hydrophobic.put("G", Integer.valueOf(1));
-    hydrophobic.put("M", Integer.valueOf(1));
-    hydrophobic.put("F", Integer.valueOf(1));
-    hydrophobic.put("Y", Integer.valueOf(1));
-    hydrophobic.put("W", Integer.valueOf(1));
-    hydrophobic.put("H", Integer.valueOf(1));
-    hydrophobic.put("K", Integer.valueOf(1));
-    hydrophobic.put("X", Integer.valueOf(1));
-    hydrophobic.put("-", Integer.valueOf(1));
-    hydrophobic.put("*", Integer.valueOf(1));
-    hydrophobic.put("R", Integer.valueOf(0));
-    hydrophobic.put("E", Integer.valueOf(0));
-    hydrophobic.put("Q", Integer.valueOf(0));
-    hydrophobic.put("D", Integer.valueOf(0));
-    hydrophobic.put("N", Integer.valueOf(0));
-    hydrophobic.put("S", Integer.valueOf(0));
-    hydrophobic.put("T", Integer.valueOf(0));
-    hydrophobic.put("P", Integer.valueOf(0));
+    hydrophobic.put("I", ONE);
+    hydrophobic.put("L", ONE);
+    hydrophobic.put("V", ONE);
+    hydrophobic.put("C", ONE);
+    hydrophobic.put("A", ONE);
+    hydrophobic.put("G", ONE);
+    hydrophobic.put("M", ONE);
+    hydrophobic.put("F", ONE);
+    hydrophobic.put("Y", ONE);
+    hydrophobic.put("W", ONE);
+    hydrophobic.put("H", ONE);
+    hydrophobic.put("K", ONE);
+    hydrophobic.put("X", ONE);
+    hydrophobic.put("-", ONE);
+    hydrophobic.put("*", ONE);
+    hydrophobic.put("R", ZERO);
+    hydrophobic.put("E", ZERO);
+    hydrophobic.put("Q", ZERO);
+    hydrophobic.put("D", ZERO);
+    hydrophobic.put("N", ZERO);
+    hydrophobic.put("S", ZERO);
+    hydrophobic.put("T", ZERO);
+    hydrophobic.put("P", ZERO);
   }
 
   static
   {
-    polar.put("Y", Integer.valueOf(1));
-    polar.put("W", Integer.valueOf(1));
-    polar.put("H", Integer.valueOf(1));
-    polar.put("K", Integer.valueOf(1));
-    polar.put("R", Integer.valueOf(1));
-    polar.put("E", Integer.valueOf(1));
-    polar.put("Q", Integer.valueOf(1));
-    polar.put("D", Integer.valueOf(1));
-    polar.put("N", Integer.valueOf(1));
-    polar.put("S", Integer.valueOf(1));
-    polar.put("T", Integer.valueOf(1));
-    polar.put("X", Integer.valueOf(1));
-    polar.put("-", Integer.valueOf(1));
-    polar.put("*", Integer.valueOf(1));
-    polar.put("I", Integer.valueOf(0));
-    polar.put("L", Integer.valueOf(0));
-    polar.put("V", Integer.valueOf(0));
-    polar.put("C", Integer.valueOf(0));
-    polar.put("A", Integer.valueOf(0));
-    polar.put("G", Integer.valueOf(0));
-    polar.put("M", Integer.valueOf(0));
-    polar.put("F", Integer.valueOf(0));
-    polar.put("P", Integer.valueOf(0));
+    polar.put("Y", ONE);
+    polar.put("W", ONE);
+    polar.put("H", ONE);
+    polar.put("K", ONE);
+    polar.put("R", ONE);
+    polar.put("E", ONE);
+    polar.put("Q", ONE);
+    polar.put("D", ONE);
+    polar.put("N", ONE);
+    polar.put("S", ONE);
+    polar.put("T", ONE);
+    polar.put("X", ONE);
+    polar.put("-", ONE);
+    polar.put("*", ONE);
+    polar.put("I", ZERO);
+    polar.put("L", ZERO);
+    polar.put("V", ZERO);
+    polar.put("C", ZERO);
+    polar.put("A", ZERO);
+    polar.put("G", ZERO);
+    polar.put("M", ZERO);
+    polar.put("F", ZERO);
+    polar.put("P", ZERO);
   }
 
   static
   {
-    small.put("I", Integer.valueOf(0));
-    small.put("L", Integer.valueOf(0));
-    small.put("V", Integer.valueOf(1));
-    small.put("C", Integer.valueOf(1));
-    small.put("A", Integer.valueOf(1));
-    small.put("G", Integer.valueOf(1));
-    small.put("M", Integer.valueOf(0));
-    small.put("F", Integer.valueOf(0));
-    small.put("Y", Integer.valueOf(0));
-    small.put("W", Integer.valueOf(0));
-    small.put("H", Integer.valueOf(0));
-    small.put("K", Integer.valueOf(0));
-    small.put("R", Integer.valueOf(0));
-    small.put("E", Integer.valueOf(0));
-    small.put("Q", Integer.valueOf(0));
-    small.put("D", Integer.valueOf(1));
-    small.put("N", Integer.valueOf(1));
-    small.put("S", Integer.valueOf(1));
-    small.put("T", Integer.valueOf(1));
-    small.put("P", Integer.valueOf(1));
-    small.put("-", Integer.valueOf(1));
-    small.put("*", Integer.valueOf(1));
+    small.put("I", ZERO);
+    small.put("L", ZERO);
+    small.put("V", ONE);
+    small.put("C", ONE);
+    small.put("A", ONE);
+    small.put("G", ONE);
+    small.put("M", ZERO);
+    small.put("F", ZERO);
+    small.put("Y", ZERO);
+    small.put("W", ZERO);
+    small.put("H", ZERO);
+    small.put("K", ZERO);
+    small.put("R", ZERO);
+    small.put("E", ZERO);
+    small.put("Q", ZERO);
+    small.put("D", ONE);
+    small.put("N", ONE);
+    small.put("S", ONE);
+    small.put("T", ONE);
+    small.put("P", ONE);
+    small.put("-", ONE);
+    small.put("*", ONE);
   }
 
   static
   {
-    positive.put("I", Integer.valueOf(0));
-    positive.put("L", Integer.valueOf(0));
-    positive.put("V", Integer.valueOf(0));
-    positive.put("C", Integer.valueOf(0));
-    positive.put("A", Integer.valueOf(0));
-    positive.put("G", Integer.valueOf(0));
-    positive.put("M", Integer.valueOf(0));
-    positive.put("F", Integer.valueOf(0));
-    positive.put("Y", Integer.valueOf(0));
-    positive.put("W", Integer.valueOf(0));
-    positive.put("H", Integer.valueOf(1));
-    positive.put("K", Integer.valueOf(1));
-    positive.put("R", Integer.valueOf(1));
-    positive.put("E", Integer.valueOf(0));
-    positive.put("Q", Integer.valueOf(0));
-    positive.put("D", Integer.valueOf(0));
-    positive.put("N", Integer.valueOf(0));
-    positive.put("S", Integer.valueOf(0));
-    positive.put("T", Integer.valueOf(0));
-    positive.put("P", Integer.valueOf(0));
-    positive.put("-", Integer.valueOf(1));
-    positive.put("*", Integer.valueOf(1));
+    positive.put("I", ZERO);
+    positive.put("L", ZERO);
+    positive.put("V", ZERO);
+    positive.put("C", ZERO);
+    positive.put("A", ZERO);
+    positive.put("G", ZERO);
+    positive.put("M", ZERO);
+    positive.put("F", ZERO);
+    positive.put("Y", ZERO);
+    positive.put("W", ZERO);
+    positive.put("H", ONE);
+    positive.put("K", ONE);
+    positive.put("R", ONE);
+    positive.put("E", ZERO);
+    positive.put("Q", ZERO);
+    positive.put("D", ZERO);
+    positive.put("N", ZERO);
+    positive.put("S", ZERO);
+    positive.put("T", ZERO);
+    positive.put("P", ZERO);
+    positive.put("-", ONE);
+    positive.put("*", ONE);
   }
 
   static
   {
-    negative.put("I", Integer.valueOf(0));
-    negative.put("L", Integer.valueOf(0));
-    negative.put("V", Integer.valueOf(0));
-    negative.put("C", Integer.valueOf(0));
-    negative.put("A", Integer.valueOf(0));
-    negative.put("G", Integer.valueOf(0));
-    negative.put("M", Integer.valueOf(0));
-    negative.put("F", Integer.valueOf(0));
-    negative.put("Y", Integer.valueOf(0));
-    negative.put("W", Integer.valueOf(0));
-    negative.put("H", Integer.valueOf(0));
-    negative.put("K", Integer.valueOf(0));
-    negative.put("R", Integer.valueOf(0));
-    negative.put("E", Integer.valueOf(1));
-    negative.put("Q", Integer.valueOf(0));
-    negative.put("D", Integer.valueOf(1));
-    negative.put("N", Integer.valueOf(0));
-    negative.put("S", Integer.valueOf(0));
-    negative.put("T", Integer.valueOf(0));
-    negative.put("P", Integer.valueOf(0));
-    negative.put("-", Integer.valueOf(1));
-    negative.put("*", Integer.valueOf(1));
+    negative.put("I", ZERO);
+    negative.put("L", ZERO);
+    negative.put("V", ZERO);
+    negative.put("C", ZERO);
+    negative.put("A", ZERO);
+    negative.put("G", ZERO);
+    negative.put("M", ZERO);
+    negative.put("F", ZERO);
+    negative.put("Y", ZERO);
+    negative.put("W", ZERO);
+    negative.put("H", ZERO);
+    negative.put("K", ZERO);
+    negative.put("R", ZERO);
+    negative.put("E", ONE);
+    negative.put("Q", ZERO);
+    negative.put("D", ONE);
+    negative.put("N", ZERO);
+    negative.put("S", ZERO);
+    negative.put("T", ZERO);
+    negative.put("P", ZERO);
+    negative.put("-", ONE);
+    negative.put("*", ONE);
   }
 
   static
   {
-    charged.put("I", Integer.valueOf(0));
-    charged.put("L", Integer.valueOf(0));
-    charged.put("V", Integer.valueOf(0));
-    charged.put("C", Integer.valueOf(0));
-    charged.put("A", Integer.valueOf(0));
-    charged.put("G", Integer.valueOf(0));
-    charged.put("M", Integer.valueOf(0));
-    charged.put("F", Integer.valueOf(0));
-    charged.put("Y", Integer.valueOf(0));
-    charged.put("W", Integer.valueOf(0));
-    charged.put("H", Integer.valueOf(1));
-    charged.put("K", Integer.valueOf(1));
-    charged.put("R", Integer.valueOf(1));
-    charged.put("E", Integer.valueOf(1));
-    charged.put("Q", Integer.valueOf(0));
-    charged.put("D", Integer.valueOf(1));
-    charged.put("N", Integer.valueOf(0)); // Asparagine is polar but not
+    charged.put("I", ZERO);
+    charged.put("L", ZERO);
+    charged.put("V", ZERO);
+    charged.put("C", ZERO);
+    charged.put("A", ZERO);
+    charged.put("G", ZERO);
+    charged.put("M", ZERO);
+    charged.put("F", ZERO);
+    charged.put("Y", ZERO);
+    charged.put("W", ZERO);
+    charged.put("H", ONE);
+    charged.put("K", ONE);
+    charged.put("R", ONE);
+    charged.put("E", ONE);
+    charged.put("Q", ZERO);
+    charged.put("D", ONE);
+    charged.put("N", ZERO); // Asparagine is polar but not
                                           // charged.
     // Alternative would be charged and
     // negative (in basic form)?
-    charged.put("S", Integer.valueOf(0));
-    charged.put("T", Integer.valueOf(0));
-    charged.put("P", Integer.valueOf(0));
-    charged.put("-", Integer.valueOf(1));
-    charged.put("*", Integer.valueOf(1));
+    charged.put("S", ZERO);
+    charged.put("T", ZERO);
+    charged.put("P", ZERO);
+    charged.put("-", ONE);
+    charged.put("*", ONE);
   }
 
   static
   {
-    aromatic.put("I", Integer.valueOf(0));
-    aromatic.put("L", Integer.valueOf(0));
-    aromatic.put("V", Integer.valueOf(0));
-    aromatic.put("C", Integer.valueOf(0));
-    aromatic.put("A", Integer.valueOf(0));
-    aromatic.put("G", Integer.valueOf(0));
-    aromatic.put("M", Integer.valueOf(0));
-    aromatic.put("F", Integer.valueOf(1));
-    aromatic.put("Y", Integer.valueOf(1));
-    aromatic.put("W", Integer.valueOf(1));
-    aromatic.put("H", Integer.valueOf(1));
-    aromatic.put("K", Integer.valueOf(0));
-    aromatic.put("R", Integer.valueOf(0));
-    aromatic.put("E", Integer.valueOf(0));
-    aromatic.put("Q", Integer.valueOf(0));
-    aromatic.put("D", Integer.valueOf(0));
-    aromatic.put("N", Integer.valueOf(0));
-    aromatic.put("S", Integer.valueOf(0));
-    aromatic.put("T", Integer.valueOf(0));
-    aromatic.put("P", Integer.valueOf(0));
-    aromatic.put("-", Integer.valueOf(1));
-    aromatic.put("*", Integer.valueOf(1));
+    aromatic.put("I", ZERO);
+    aromatic.put("L", ZERO);
+    aromatic.put("V", ZERO);
+    aromatic.put("C", ZERO);
+    aromatic.put("A", ZERO);
+    aromatic.put("G", ZERO);
+    aromatic.put("M", ZERO);
+    aromatic.put("F", ONE);
+    aromatic.put("Y", ONE);
+    aromatic.put("W", ONE);
+    aromatic.put("H", ONE);
+    aromatic.put("K", ZERO);
+    aromatic.put("R", ZERO);
+    aromatic.put("E", ZERO);
+    aromatic.put("Q", ZERO);
+    aromatic.put("D", ZERO);
+    aromatic.put("N", ZERO);
+    aromatic.put("S", ZERO);
+    aromatic.put("T", ZERO);
+    aromatic.put("P", ZERO);
+    aromatic.put("-", ONE);
+    aromatic.put("*", ONE);
   }
 
   static
   {
-    aliphatic.put("I", Integer.valueOf(1));
-    aliphatic.put("L", Integer.valueOf(1));
-    aliphatic.put("V", Integer.valueOf(1));
-    aliphatic.put("C", Integer.valueOf(0));
-    aliphatic.put("A", Integer.valueOf(0));
-    aliphatic.put("G", Integer.valueOf(0));
-    aliphatic.put("M", Integer.valueOf(0));
-    aliphatic.put("F", Integer.valueOf(0));
-    aliphatic.put("Y", Integer.valueOf(0));
-    aliphatic.put("W", Integer.valueOf(0));
-    aliphatic.put("H", Integer.valueOf(0));
-    aliphatic.put("K", Integer.valueOf(0));
-    aliphatic.put("R", Integer.valueOf(0));
-    aliphatic.put("E", Integer.valueOf(0));
-    aliphatic.put("Q", Integer.valueOf(0));
-    aliphatic.put("D", Integer.valueOf(0));
-    aliphatic.put("N", Integer.valueOf(0));
-    aliphatic.put("S", Integer.valueOf(0));
-    aliphatic.put("T", Integer.valueOf(0));
-    aliphatic.put("P", Integer.valueOf(0));
-    aliphatic.put("-", Integer.valueOf(1));
-    aliphatic.put("*", Integer.valueOf(1));
+    aliphatic.put("I", ONE);
+    aliphatic.put("L", ONE);
+    aliphatic.put("V", ONE);
+    aliphatic.put("C", ZERO);
+    aliphatic.put("A", ZERO);
+    aliphatic.put("G", ZERO);
+    aliphatic.put("M", ZERO);
+    aliphatic.put("F", ZERO);
+    aliphatic.put("Y", ZERO);
+    aliphatic.put("W", ZERO);
+    aliphatic.put("H", ZERO);
+    aliphatic.put("K", ZERO);
+    aliphatic.put("R", ZERO);
+    aliphatic.put("E", ZERO);
+    aliphatic.put("Q", ZERO);
+    aliphatic.put("D", ZERO);
+    aliphatic.put("N", ZERO);
+    aliphatic.put("S", ZERO);
+    aliphatic.put("T", ZERO);
+    aliphatic.put("P", ZERO);
+    aliphatic.put("-", ONE);
+    aliphatic.put("*", ONE);
   }
 
   static
   {
-    tiny.put("I", Integer.valueOf(0));
-    tiny.put("L", Integer.valueOf(0));
-    tiny.put("V", Integer.valueOf(0));
-    tiny.put("C", Integer.valueOf(0));
-    tiny.put("A", Integer.valueOf(1));
-    tiny.put("G", Integer.valueOf(1));
-    tiny.put("M", Integer.valueOf(0));
-    tiny.put("F", Integer.valueOf(0));
-    tiny.put("Y", Integer.valueOf(0));
-    tiny.put("W", Integer.valueOf(0));
-    tiny.put("H", Integer.valueOf(0));
-    tiny.put("K", Integer.valueOf(0));
-    tiny.put("R", Integer.valueOf(0));
-    tiny.put("E", Integer.valueOf(0));
-    tiny.put("Q", Integer.valueOf(0));
-    tiny.put("D", Integer.valueOf(0));
-    tiny.put("N", Integer.valueOf(0));
-    tiny.put("S", Integer.valueOf(1));
-    tiny.put("T", Integer.valueOf(0));
-    tiny.put("P", Integer.valueOf(0));
-    tiny.put("-", Integer.valueOf(1));
-    tiny.put("*", Integer.valueOf(1));
+    tiny.put("I", ZERO);
+    tiny.put("L", ZERO);
+    tiny.put("V", ZERO);
+    tiny.put("C", ZERO);
+    tiny.put("A", ONE);
+    tiny.put("G", ONE);
+    tiny.put("M", ZERO);
+    tiny.put("F", ZERO);
+    tiny.put("Y", ZERO);
+    tiny.put("W", ZERO);
+    tiny.put("H", ZERO);
+    tiny.put("K", ZERO);
+    tiny.put("R", ZERO);
+    tiny.put("E", ZERO);
+    tiny.put("Q", ZERO);
+    tiny.put("D", ZERO);
+    tiny.put("N", ZERO);
+    tiny.put("S", ONE);
+    tiny.put("T", ZERO);
+    tiny.put("P", ZERO);
+    tiny.put("-", ONE);
+    tiny.put("*", ONE);
   }
 
   static
   {
-    proline.put("I", Integer.valueOf(0));
-    proline.put("L", Integer.valueOf(0));
-    proline.put("V", Integer.valueOf(0));
-    proline.put("C", Integer.valueOf(0));
-    proline.put("A", Integer.valueOf(0));
-    proline.put("G", Integer.valueOf(0));
-    proline.put("M", Integer.valueOf(0));
-    proline.put("F", Integer.valueOf(0));
-    proline.put("Y", Integer.valueOf(0));
-    proline.put("W", Integer.valueOf(0));
-    proline.put("H", Integer.valueOf(0));
-    proline.put("K", Integer.valueOf(0));
-    proline.put("R", Integer.valueOf(0));
-    proline.put("E", Integer.valueOf(0));
-    proline.put("Q", Integer.valueOf(0));
-    proline.put("D", Integer.valueOf(0));
-    proline.put("N", Integer.valueOf(0));
-    proline.put("S", Integer.valueOf(0));
-    proline.put("T", Integer.valueOf(0));
-    proline.put("P", Integer.valueOf(1));
-    proline.put("-", Integer.valueOf(1));
-    proline.put("*", Integer.valueOf(1));
+    proline.put("I", ZERO);
+    proline.put("L", ZERO);
+    proline.put("V", ZERO);
+    proline.put("C", ZERO);
+    proline.put("A", ZERO);
+    proline.put("G", ZERO);
+    proline.put("M", ZERO);
+    proline.put("F", ZERO);
+    proline.put("Y", ZERO);
+    proline.put("W", ZERO);
+    proline.put("H", ZERO);
+    proline.put("K", ZERO);
+    proline.put("R", ZERO);
+    proline.put("E", ZERO);
+    proline.put("Q", ZERO);
+    proline.put("D", ZERO);
+    proline.put("N", ZERO);
+    proline.put("S", ZERO);
+    proline.put("T", ZERO);
+    proline.put("P", ONE);
+    proline.put("-", ONE);
+    proline.put("*", ONE);
   }
 
   static
index e6aa472..04cb75e 100755 (executable)
@@ -100,14 +100,14 @@ public class DBRefUtils
     HashSet<String> srcs = new HashSet<String>();
     for (String src : sources)
     {
-      srcs.add(src);
+      srcs.add(src.toUpperCase());
     }
 
     List<DBRefEntry> res = new ArrayList<DBRefEntry>();
     for (DBRefEntry dbr : dbrefs)
     {
       String source = getCanonicalName(dbr.getSource());
-      if (srcs.contains(source))
+      if (srcs.contains(source.toUpperCase()))
       {
         res.add(dbr);
       }
index 7121985..389afcd 100755 (executable)
@@ -26,6 +26,8 @@
  */
 package jalview.util;
 
+import java.util.Arrays;
+
 /**
  * DOCUMENT ME!
  * 
@@ -664,30 +666,22 @@ public class Format
   }
 
   /**
-   * DOCUMENT ME!
+   * Returns a string consisting of n repeats of character c
    * 
    * @param c
-   *          DOCUMENT ME!
    * @param n
-   *          DOCUMENT ME!
    * 
-   * @return DOCUMENT ME!
+   * @return
    */
-  private static String repeat(char c, int n)
+  static String repeat(char c, int n)
   {
     if (n <= 0)
     {
       return "";
     }
-
-    StringBuffer s = new StringBuffer(n);
-
-    for (int i = 0; i < n; i++)
-    {
-      s.append(c);
-    }
-
-    return s.toString();
+    char[] chars = new char[n];
+    Arrays.fill(chars, c);
+    return new String(chars);
   }
 
   /**
@@ -959,7 +953,27 @@ public class Format
    */
   public static void appendPercentage(StringBuilder sb, float value, int dp)
   {
-    sb.append((int) value);
+    /*
+     * rounding first
+     */
+    double d = value;
+    long factor = 1L;
+    for (int i = 0; i < dp; i++)
+    {
+      factor *= 10;
+    }
+    d *= factor;
+    d += 0.5;
+
+    /*
+     * integer part
+     */
+    value = (float) (d / factor);
+    sb.append((long) value);
+
+    /*
+     * decimal places
+     */
     if (dp > 0)
     {
       sb.append(".");
index ce6d980..1910bff 100644 (file)
@@ -29,22 +29,17 @@ public class UrlConstants
   /*
    * Sequence ID string
    */
-  public static final String SEQUENCE_ID = "SEQUENCE_ID";
+  public static final String DB_ACCESSION = "DB_ACCESSION";
 
   /*
    * Sequence Name string
    */
-  public static final String SEQUENCE_NAME = "SEQUENCE_NAME";
-
-  /*
-   * Default sequence URL link string for EMBL-EBI search
-   */
-  public static final String EMBLEBI_STRING = "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_NAME$";
+  public static final String SEQUENCE_ID = "SEQUENCE_ID";
 
   /*
    * Default sequence URL link string for EMBL-EBI search
    */
-  public static final String OLD_EMBLEBI_STRING = "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$";
+  public static final String EMBLEBI_STRING = "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$";
 
   /*
    * Default sequence URL link string for SRS 
index 872f432..3ee6432 100644 (file)
  */
 package jalview.util;
 
+import static jalview.util.UrlConstants.DB_ACCESSION;
 import static jalview.util.UrlConstants.SEQUENCE_ID;
-import static jalview.util.UrlConstants.SEQUENCE_NAME;
 
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.SequenceI;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
 public class UrlLink
@@ -33,19 +39,33 @@ public class UrlLink
    * Jalview 2.4 extension allows regular expressions to be used to parse ID
    * strings and replace the result in the URL. Regex's operate on the whole ID
    * string given to the matchURL method, if no regex is supplied, then only
-   * text following the first pipe symbol will be susbstituted. Usage
+   * text following the first pipe symbol will be substituted. Usage
    * documentation todo.
    */
-  private String url_suffix, url_prefix, target, label, regexReplace;
+
+  // Internal constants
+  private static final String SEP = "|";
+
+  private static final String DELIM = "$";
+
+  private String urlSuffix;
+
+  private String urlPrefix;
+
+  private String target;
+
+  private String label;
+
+  private String regexReplace;
 
   private boolean dynamic = false;
 
-  private boolean uses_seq_id = false;
+  private boolean usesDBaccession = false;
 
   private String invalidMessage = null;
 
   /**
-   * parse the given linkString of the form '<label>|<url>' into parts url may
+   * parse the given linkString of the form '<label>SEP<url>' into parts url may
    * contain a string $SEQUENCE_ID<=optional regex=>$ where <=optional regex=>
    * must be of the form =/<perl style regex>/=$
    * 
@@ -53,40 +73,37 @@ public class UrlLink
    */
   public UrlLink(String link)
   {
-    int sep = link.indexOf("|");
-    int psqid = link.indexOf("$" + SEQUENCE_ID);
-    int nsqid = link.indexOf("$" + SEQUENCE_NAME);
+    int sep = link.indexOf(SEP);
+    int psqid = link.indexOf(DELIM + DB_ACCESSION);
+    int nsqid = link.indexOf(DELIM + SEQUENCE_ID);
     if (psqid > -1)
     {
       dynamic = true;
-      uses_seq_id = true;
+      usesDBaccession = true;
 
       sep = parseTargetAndLabel(sep, psqid, link);
 
-      parseUrl(link, SEQUENCE_ID, psqid, sep);
+      parseUrl(link, DB_ACCESSION, psqid, sep);
     }
     else if (nsqid > -1)
     {
       dynamic = true;
       sep = parseTargetAndLabel(sep, nsqid, link);
 
-      parseUrl(link, SEQUENCE_NAME, nsqid, sep);
+      parseUrl(link, SEQUENCE_ID, nsqid, sep);
     }
     else
     {
       target = link.substring(0, sep);
-      sep = link.lastIndexOf("|");
+      sep = link.lastIndexOf(SEP);
       label = link.substring(0, sep);
-      url_prefix = link.substring(sep + 1);
+      urlPrefix = link.substring(sep + 1).trim();
       regexReplace = null; // implies we trim any prefix if necessary //
-      // regexReplace=".*\\|?(.*)";
-      url_suffix = null;
+      urlSuffix = null;
     }
 
     label = label.trim();
     target = target.trim();
-    target = target.toUpperCase(); // DBRefEntry uppercases DB names
-    // NB getCanonicalName might be better but does not currently change case
   }
 
   /**
@@ -94,7 +111,7 @@ public class UrlLink
    */
   public String getUrl_suffix()
   {
-    return url_suffix;
+    return urlSuffix;
   }
 
   /**
@@ -102,7 +119,7 @@ public class UrlLink
    */
   public String getUrl_prefix()
   {
-    return url_prefix;
+    return urlPrefix;
   }
 
   /**
@@ -149,6 +166,34 @@ public class UrlLink
   }
 
   /**
+   * 
+   * @return whether link is dynamic
+   */
+  public boolean isDynamic()
+  {
+    return dynamic;
+  }
+
+  /**
+   * 
+   * @return whether link uses DB Accession id
+   */
+  public boolean usesDBAccession()
+  {
+    return usesDBaccession;
+  }
+
+  /**
+   * Set the label
+   * 
+   * @param newlabel
+   */
+  public void setLabel(String newlabel)
+  {
+    this.label = newlabel;
+  }
+
+  /**
    * return one or more URL strings by applying regex to the given idstring
    * 
    * @param idstring
@@ -173,7 +218,7 @@ public class UrlLink
           {
             // take whole regex
             return new String[] { rg.stringMatched(),
-                url_prefix + rg.stringMatched() + url_suffix };
+                urlPrefix + rg.stringMatched() + urlSuffix };
           } /*
              * else if (ns==1) { // take only subgroup match return new String[]
              * { rg.stringMatched(1), url_prefix+rg.stringMatched(1)+url_suffix
@@ -214,7 +259,7 @@ public class UrlLink
                 if (mtch.length() > 0)
                 {
                   subs.addElement(mtch);
-                  subs.addElement(url_prefix + mtch + url_suffix);
+                  subs.addElement(urlPrefix + mtch + urlSuffix);
                 }
                 s = r;
               }
@@ -223,8 +268,8 @@ public class UrlLink
                 if (rg.matchedFrom(s) > -1)
                 {
                   subs.addElement(rg.stringMatched(s));
-                  subs.addElement(url_prefix + rg.stringMatched(s)
-                          + url_suffix);
+                  subs.addElement(urlPrefix + rg.stringMatched(s)
+                          + urlSuffix);
                 }
                 s++;
               }
@@ -245,31 +290,31 @@ public class UrlLink
         }
       }
       /* Otherwise - trim off any 'prefix' - pre 2.4 Jalview behaviour */
-      if (idstring.indexOf("|") > -1)
+      if (idstring.indexOf(SEP) > -1)
       {
-        idstring = idstring.substring(idstring.lastIndexOf("|") + 1);
+        idstring = idstring.substring(idstring.lastIndexOf(SEP) + 1);
       }
 
       // just return simple url substitution.
-      return new String[] { idstring, url_prefix + idstring + url_suffix };
+      return new String[] { idstring, urlPrefix + idstring + urlSuffix };
     }
     else
     {
-      return new String[] { "", url_prefix };
+      return new String[] { "", urlPrefix };
     }
   }
 
   @Override
   public String toString()
   {
-    String var = (uses_seq_id ? SEQUENCE_ID : SEQUENCE_NAME);
+    String var = (usesDBaccession ? DB_ACCESSION : SEQUENCE_ID);
 
     return label
-            + "|"
-            + url_prefix
-            + (dynamic ? ("$" + var + ((regexReplace != null) ? "="
-                    + regexReplace + "=$" : "$")) : "")
-            + ((url_suffix == null) ? "" : url_suffix);
+            + SEP
+            + urlPrefix
+            + (dynamic ? (DELIM + var + ((regexReplace != null) ? "="
+                    + regexReplace + "=" + DELIM : DELIM)) : "")
+            + ((urlSuffix == null) ? "" : urlSuffix);
   }
 
   /**
@@ -289,15 +334,15 @@ public class UrlLink
     do
     {
       sep = p;
-      p = link.indexOf("|", sep + 1);
+      p = link.indexOf(SEP, sep + 1);
     } while (p > sep && p < psqid);
-    // Assuming that the URL itself does not contain any '|' symbols
+    // Assuming that the URL itself does not contain any SEP symbols
     // sep now contains last pipe symbol position prior to any regex symbols
     label = link.substring(0, sep);
-    if (label.indexOf("|") > -1)
+    if (label.indexOf(SEP) > -1)
     {
-      // | terminated database name / www target at start of Label
-      target = label.substring(0, label.indexOf("|"));
+      // SEP terminated database name / www target at start of Label
+      target = label.substring(0, label.indexOf(SEP));
     }
     else if (label.indexOf(" ") > 2)
     {
@@ -325,13 +370,13 @@ public class UrlLink
    */
   protected void parseUrl(String link, String varName, int sqidPos, int sep)
   {
-    url_prefix = link.substring(sep + 1, sqidPos);
+    urlPrefix = link.substring(sep + 1, sqidPos).trim();
 
     // delimiter at start of regex: e.g. $SEQUENCE_ID=/
-    String startDelimiter = "$" + varName + "=/";
+    String startDelimiter = DELIM + varName + "=/";
 
     // delimiter at end of regex: /=$
-    String endDelimiter = "/=$";
+    String endDelimiter = "/=" + DELIM;
 
     int startLength = startDelimiter.length();
 
@@ -342,7 +387,7 @@ public class UrlLink
             && (p > sqidPos + startLength))
     {
       // Extract Regex and suffix
-      url_suffix = link.substring(p + endDelimiter.length());
+      urlSuffix = link.substring(p + endDelimiter.length());
       regexReplace = link.substring(sqidPos + startLength, p);
       try
       {
@@ -364,9 +409,9 @@ public class UrlLink
       // no regex
       regexReplace = null;
       // verify format is really correct.
-      if (link.indexOf("$" + varName + "$") == sqidPos)
+      if (link.indexOf(DELIM + varName + DELIM) == sqidPos)
       {
-        url_suffix = link.substring(sqidPos + startLength - 1);
+        urlSuffix = link.substring(sqidPos + startLength - 1);
         regexReplace = null;
       }
       else
@@ -377,6 +422,124 @@ public class UrlLink
     }
   }
 
+  /**
+   * Create a set of URL links for a sequence
+   * 
+   * @param seq
+   *          The sequence to create links for
+   * @param linkset
+   *          Map of links: key = id + SEP + link, value = [target, label, id,
+   *          link]
+   */
+  public void createLinksFromSeq(final SequenceI seq,
+          Map<String, List<String>> linkset)
+  {
+    if (seq != null && dynamic)
+    {
+      createDynamicLinks(seq, linkset);
+    }
+    else
+    {
+      createStaticLink(linkset);
+    }
+  }
+
+  /**
+   * Create a static URL link
+   * 
+   * @param linkset
+   *          Map of links: key = id + SEP + link, value = [target, label, id,
+   *          link]
+   */
+  protected void createStaticLink(Map<String, List<String>> linkset)
+  {
+    if (!linkset.containsKey(label + SEP + getUrl_prefix()))
+    {
+      // Add a non-dynamic link
+      linkset.put(label + SEP + getUrl_prefix(),
+              Arrays.asList(target, label, null, getUrl_prefix()));
+    }
+  }
+
+  /**
+   * Create dynamic URL links
+   * 
+   * @param seq
+   *          The sequence to create links for
+   * @param linkset
+   *          Map of links: key = id + SEP + link, value = [target, label, id,
+   *          link]
+   */
+  protected void createDynamicLinks(final SequenceI seq,
+          Map<String, List<String>> linkset)
+  {
+    // collect id string too
+    String id = seq.getName();
+    String descr = seq.getDescription();
+    if (descr != null && descr.length() < 1)
+    {
+      descr = null;
+    }
+
+    if (usesDBAccession()) // link is ID
+    {
+      // collect matching db-refs
+      DBRefEntry[] dbr = DBRefUtils.selectRefs(seq.getDBRefs(),
+              new String[] { target });
+
+      // if there are any dbrefs which match up with the link
+      if (dbr != null)
+      {
+        for (int r = 0; r < dbr.length; r++)
+        {
+          // create Bare ID link for this URL
+          createBareURLLink(dbr[r].getAccessionId(), true, linkset);
+        }
+      }
+    }
+    else if (!usesDBAccession() && id != null) // link is name
+    {
+      // create Bare ID link for this URL
+      createBareURLLink(id, false, linkset);
+    }
+
+    // Create urls from description but only for URL links which are regex
+    // links
+    if (descr != null && getRegexReplace() != null)
+    {
+      // create link for this URL from description where regex matches
+      createBareURLLink(descr, false, linkset);
+    }
+  }
+
+  /*
+   * Create a bare URL Link
+   * Returns map where key = id + SEP + link, and value = [target, label, id, link]
+   */
+  protected void createBareURLLink(String id, Boolean combineLabel,
+          Map<String, List<String>> linkset)
+  {
+    String[] urls = makeUrls(id, true);
+    if (urls != null)
+    {
+      for (int u = 0; u < urls.length; u += 2)
+      {
+        if (!linkset.containsKey(urls[u] + SEP + urls[u + 1]))
+        {
+          String thisLabel = label;
+          if (combineLabel)
+          {
+            // incorporate label with idstring
+            thisLabel = label + SEP + urls[u];
+          }
+
+          linkset.put(urls[u] + SEP + urls[u + 1],
+                  Arrays.asList(target, thisLabel, urls[u], urls[u + 1]));
+        }
+      }
+    }
+  }
+
   private static void testUrls(UrlLink ul, String idstring, String[] urls)
   {
 
@@ -412,7 +575,7 @@ public class UrlLink
      * "PF3|http://us.expasy.org/cgi-bin/niceprot.pl?$SEQUENCE_ID=/PFAM:(.+)/=$"
      * , "NOTFER|http://notfer.org/$SEQUENCE_ID=/(?<!\\s)(.+)/=$",
      */
-    "NESTED|http://nested/$" + SEQUENCE_ID
+    "NESTED|http://nested/$" + DB_ACCESSION
             + "=/^(?:Label:)?(?:(?:gi\\|(\\d+))|([^:]+))/=$/nested" };
     String[] idstrings = new String[] {
     /*
@@ -454,20 +617,4 @@ public class UrlLink
       }
     }
   }
-
-  public boolean isDynamic()
-  {
-    // TODO Auto-generated method stub
-    return dynamic;
-  }
-
-  public boolean usesSeqId()
-  {
-    return uses_seq_id;
-  }
-
-  public void setLabel(String newlabel)
-  {
-    this.label = newlabel;
-  }
 }
index c01be4e..57258ef 100644 (file)
@@ -22,7 +22,6 @@ package jalview.viewmodel;
 
 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
 import jalview.analysis.Conservation;
-import jalview.analysis.Profile;
 import jalview.api.AlignCalcManagerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
@@ -37,6 +36,7 @@ import jalview.datamodel.Annotation;
 import jalview.datamodel.CigarArray;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.HiddenSequences;
+import jalview.datamodel.ProfileI;
 import jalview.datamodel.SearchResults;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceCollectionI;
@@ -701,7 +701,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   /**
    * results of alignment consensus analysis for visible portion of view
    */
-  protected Profile[] hconsensus = null;
+  protected ProfileI[] hconsensus = null;
 
   /**
    * results of cDNA complement consensus visible portion of view
@@ -735,7 +735,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public void setSequenceConsensusHash(Profile[] hconsensus)
+  public void setSequenceConsensusHash(ProfileI[] hconsensus)
   {
     this.hconsensus = hconsensus;
   }
@@ -747,7 +747,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
   }
 
   @Override
-  public Profile[] getSequenceConsensusHash()
+  public ProfileI[] getSequenceConsensusHash()
   {
     return hconsensus;
   }
@@ -1867,7 +1867,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
       cs.setConsensus(hconsensus);
       if (cs.conservationApplied())
       {
-        cs.setConservation(Conservation.calculateConservation("All", 3,
+        cs.setConservation(Conservation.calculateConservation("All",
                 alignment.getSequences(), 0, alignment.getWidth(), false,
                 getConsPercGaps(), false));
       }
index c88a24a..2b11477 100644 (file)
 package jalview.workers;
 
 import jalview.analysis.AAFrequency;
-import jalview.analysis.Profile;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.ProfileI;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
 
@@ -124,7 +124,7 @@ public class ConsensusThread extends AlignCalcWorker
    */
   protected void computeConsensus(AlignmentI alignment)
   {
-    Profile[] hconsensus = new Profile[alignment.getWidth()];
+    ProfileI[] hconsensus = new ProfileI[alignment.getWidth()];
 
     SequenceI[] aseqs = getSequences();
     AAFrequency.calculate(aseqs, 0, alignment.getWidth(), hconsensus, true);
@@ -144,7 +144,7 @@ public class ConsensusThread extends AlignCalcWorker
   /**
    * @param hconsensus
    */
-  protected void setColourSchemeConsensus(Profile[] hconsensus)
+  protected void setColourSchemeConsensus(ProfileI[] hconsensus)
   {
     ColourSchemeI globalColourScheme = alignViewport
             .getGlobalColourScheme();
@@ -177,7 +177,7 @@ public class ConsensusThread extends AlignCalcWorker
   public void updateResultAnnotation(boolean immediate)
   {
     AlignmentAnnotation consensus = getConsensusAnnotation();
-    Profile[] hconsensus = (Profile[]) getViewportConsensus();
+    ProfileI[] hconsensus = (ProfileI[]) getViewportConsensus();
     if (immediate || !calcMan.isWorking(this) && consensus != null
             && hconsensus != null)
     {
@@ -195,7 +195,7 @@ public class ConsensusThread extends AlignCalcWorker
    *          the computed consensus data
    */
   protected void deriveConsensus(AlignmentAnnotation consensusAnnotation,
-          Profile[] hconsensus)
+          ProfileI[] hconsensus)
   {
     long nseq = getSequences().length;
     AAFrequency.completeConsensus(consensusAnnotation, hconsensus, 0,
index 11ec521..e71c4f5 100644 (file)
@@ -94,7 +94,7 @@ public class ConservationThread extends AlignCalcWorker
       }
       try
       {
-        cons = Conservation.calculateConservation("All", 3,
+        cons = Conservation.calculateConservation("All",
                 alignment.getSequences(), 0, alWidth - 1, false,
                 ConsPercGaps, quality != null);
       } catch (IndexOutOfBoundsException x)
index 0ddbddc..1c04b8e 100644 (file)
@@ -25,6 +25,7 @@ import static org.testng.AssertJUnit.assertNull;
 
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.ProfileI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 
@@ -40,12 +41,12 @@ public class AAFrequencyTest
     SequenceI seq3 = new Sequence("Seq3", "C---G");
     SequenceI seq4 = new Sequence("Seq4", "CA--t");
     SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 };
-    Profile[] result = new Profile[seq1.getLength()];
+    ProfileI[] result = new ProfileI[seq1.getLength()];
 
     AAFrequency.calculate(seqs, 0, seq1.getLength(), result, false);
 
     // col 0 is 100% C
-    Profile col = result[0];
+    ProfileI col = result[0];
     assertEquals(100f, col.getPercentageIdentity(false));
     assertEquals(100f, col.getPercentageIdentity(true));
     assertEquals(4, col.getMaxCount());
@@ -89,10 +90,10 @@ public class AAFrequencyTest
     SequenceI seq3 = new Sequence("Seq3", "C--G");
     SequenceI seq4 = new Sequence("Seq4", "CA-t");
     SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 };
-    Profile[] result = new Profile[seq1.getLength()];
+    ProfileI[] result = new ProfileI[seq1.getLength()];
 
     AAFrequency.calculate(seqs, 0, seq1.getLength(), result, true);
-    Profile profile = result[0];
+    ProfileI profile = result[0];
     assertEquals(4, profile.getCounts().getCount('C'));
     assertEquals(4, profile.getHeight());
     assertEquals(4, profile.getNonGapped());
@@ -123,7 +124,7 @@ public class AAFrequencyTest
     SequenceI seq3 = new Sequence("Seq3", "C--G");
     SequenceI seq4 = new Sequence("Seq4", "CA-t");
     SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 };
-    Profile[] result = new Profile[seq1.getLength()];
+    ProfileI[] result = new ProfileI[seq1.getLength()];
 
     // ensure class loaded and initialized
     AAFrequency.calculate(seqs, 0, seq1.getLength(), result, true);
@@ -153,7 +154,7 @@ public class AAFrequencyTest
     SequenceI seq3 = new Sequence("Seq3", "C---G");
     SequenceI seq4 = new Sequence("Seq4", "CA--t");
     SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 };
-    Profile[] profiles = new Profile[seq1.getLength()];
+    ProfileI[] profiles = new ProfileI[seq1.getLength()];
     AAFrequency.calculate(seqs, 0, seq1.getLength(), profiles, true);
 
     AlignmentAnnotation consensus = new AlignmentAnnotation("Consensus",
@@ -194,7 +195,7 @@ public class AAFrequencyTest
     SequenceI seq3 = new Sequence("Seq3", "C---G");
     SequenceI seq4 = new Sequence("Seq4", "CA--t");
     SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 };
-    Profile[] profiles = new Profile[seq1.getLength()];
+    ProfileI[] profiles = new ProfileI[seq1.getLength()];
     AAFrequency.calculate(seqs, 0, seq1.getLength(), profiles, true);
   
     AlignmentAnnotation consensus = new AlignmentAnnotation("Consensus",
diff --git a/test/jalview/analysis/ConservationTest.java b/test/jalview/analysis/ConservationTest.java
new file mode 100644 (file)
index 0000000..123fcd6
--- /dev/null
@@ -0,0 +1,316 @@
+package jalview.analysis;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.annotations.Test;
+
+public class ConservationTest
+{
+  @Test(groups = "Functional")
+  public void testRecordConservation()
+  {
+    Map<String, Integer> resultMap = new HashMap<String, Integer>();
+
+    // V is hydrophobic, aliphatic, small
+    Conservation.recordConservation(resultMap, "V");
+    assertEquals(resultMap.get("hydrophobic").intValue(), 1);
+    assertEquals(resultMap.get("aliphatic").intValue(), 1);
+    assertEquals(resultMap.get("small").intValue(), 1);
+    assertEquals(resultMap.get("tiny").intValue(), 0);
+    assertEquals(resultMap.get("polar").intValue(), 0);
+    assertEquals(resultMap.get("charged").intValue(), 0);
+
+    // now add S: not hydrophobic, small, tiny, polar, not aliphatic
+    Conservation.recordConservation(resultMap, "s");
+    assertEquals(resultMap.get("hydrophobic").intValue(), -1);
+    assertEquals(resultMap.get("aliphatic").intValue(), -1);
+    assertEquals(resultMap.get("small").intValue(), 1);
+    assertEquals(resultMap.get("tiny").intValue(), -1);
+    assertEquals(resultMap.get("polar").intValue(), -1);
+    assertEquals(resultMap.get("charged").intValue(), 0);
+  }
+
+  @Test(groups = "Functional")
+  public void testCountConservationAndGaps()
+  {
+    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    seqs.add(new Sequence("seq1", "VGnY")); // not case sensitive
+    seqs.add(new Sequence("seq2", "-G-y"));
+    seqs.add(new Sequence("seq3", "VG-Y"));
+    seqs.add(new Sequence("seq4", "VGNW"));
+
+    Conservation cons = new Conservation("", seqs, 0, 50);
+    int[] counts = cons.countConservationAndGaps(0);
+    assertEquals(counts[0], 1); // conserved
+    assertEquals(counts[1], 1); // gap count
+    counts = cons.countConservationAndGaps(1);
+    assertEquals(counts[0], 1);
+    assertEquals(counts[1], 0);
+    counts = cons.countConservationAndGaps(2);
+    assertEquals(counts[0], 1);
+    assertEquals(counts[1], 2);
+    counts = cons.countConservationAndGaps(3);
+    assertEquals(counts[0], 0); // not conserved
+    assertEquals(counts[1], 0);
+  }
+
+  @Test(groups = "Functional")
+  public void testCalculate_noThreshold()
+  {
+    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    seqs.add(new Sequence("seq1", "VGIV-N"));
+    seqs.add(new Sequence("seq2", "V-iL-N")); // not case sensitive
+    seqs.add(new Sequence("seq3", "V-IW-N"));
+    seqs.add(new Sequence("seq4", "VGLH-L"));
+
+    Conservation cons = new Conservation("", 0, seqs, 0, 5);
+    cons.calculate();
+
+    /*
+     * column 0: all V (hydrophobic/aliphatic/small)
+     */
+    Map<String, Integer> colCons = cons.total[0];
+    assertEquals(colCons.get("hydrophobic").intValue(), 1);
+    assertEquals(colCons.get("aliphatic").intValue(), 1);
+    assertEquals(colCons.get("small").intValue(), 1);
+    assertEquals(colCons.get("tiny").intValue(), 0);
+    assertEquals(colCons.get("proline").intValue(), 0);
+    assertEquals(colCons.get("charged").intValue(), 0);
+    assertEquals(colCons.get("negative").intValue(), 0);
+    assertEquals(colCons.get("polar").intValue(), 0);
+    assertEquals(colCons.get("positive").intValue(), 0);
+    assertEquals(colCons.get("aromatic").intValue(), 0);
+
+    /*
+     * column 1: all G (hydrophobic/small/tiny)
+     * gaps take default value of property present
+     */
+    colCons = cons.total[1];
+    assertEquals(colCons.get("hydrophobic").intValue(), 1);
+    assertEquals(colCons.get("aliphatic").intValue(), -1);
+    assertEquals(colCons.get("small").intValue(), 1);
+    assertEquals(colCons.get("tiny").intValue(), 1);
+    assertEquals(colCons.get("proline").intValue(), -1);
+    assertEquals(colCons.get("charged").intValue(), -1);
+    assertEquals(colCons.get("negative").intValue(), -1);
+    assertEquals(colCons.get("polar").intValue(), -1);
+    assertEquals(colCons.get("positive").intValue(), -1);
+    assertEquals(colCons.get("aromatic").intValue(), -1);
+
+    /*
+     * column 2: I/L (aliphatic/hydrophobic), all others negatively conserved
+     */
+    colCons = cons.total[2];
+    assertEquals(colCons.get("hydrophobic").intValue(), 1);
+    assertEquals(colCons.get("aliphatic").intValue(), 1);
+    assertEquals(colCons.get("small").intValue(), 0);
+    assertEquals(colCons.get("tiny").intValue(), 0);
+    assertEquals(colCons.get("proline").intValue(), 0);
+    assertEquals(colCons.get("charged").intValue(), 0);
+    assertEquals(colCons.get("negative").intValue(), 0);
+    assertEquals(colCons.get("polar").intValue(), 0);
+    assertEquals(colCons.get("positive").intValue(), 0);
+    assertEquals(colCons.get("aromatic").intValue(), 0);
+
+    /*
+     * column 3: VLWH all hydrophobic, none is tiny, negative or proline
+     */
+    colCons = cons.total[3];
+    assertEquals(colCons.get("hydrophobic").intValue(), 1);
+    assertEquals(colCons.get("aliphatic").intValue(), -1);
+    assertEquals(colCons.get("small").intValue(), -1);
+    assertEquals(colCons.get("tiny").intValue(), 0);
+    assertEquals(colCons.get("proline").intValue(), 0);
+    assertEquals(colCons.get("charged").intValue(), -1);
+    assertEquals(colCons.get("negative").intValue(), 0);
+    assertEquals(colCons.get("polar").intValue(), -1);
+    assertEquals(colCons.get("positive").intValue(), -1);
+    assertEquals(colCons.get("aromatic").intValue(), -1);
+
+    /*
+     * column 4: all gaps - counted as having all properties
+     */
+    colCons = cons.total[4];
+    assertEquals(colCons.get("hydrophobic").intValue(), 1);
+    assertEquals(colCons.get("aliphatic").intValue(), 1);
+    assertEquals(colCons.get("small").intValue(), 1);
+    assertEquals(colCons.get("tiny").intValue(), 1);
+    assertEquals(colCons.get("proline").intValue(), 1);
+    assertEquals(colCons.get("charged").intValue(), 1);
+    assertEquals(colCons.get("negative").intValue(), 1);
+    assertEquals(colCons.get("polar").intValue(), 1);
+    assertEquals(colCons.get("positive").intValue(), 1);
+    assertEquals(colCons.get("aromatic").intValue(), 1);
+
+    /*
+     * column 5: N (small polar) and L (aliphatic hydrophobic) 
+     * have nothing in common!
+     */
+    colCons = cons.total[5];
+    assertEquals(colCons.get("hydrophobic").intValue(), -1);
+    assertEquals(colCons.get("aliphatic").intValue(), -1);
+    assertEquals(colCons.get("small").intValue(), -1);
+    assertEquals(colCons.get("tiny").intValue(), 0);
+    assertEquals(colCons.get("proline").intValue(), 0);
+    assertEquals(colCons.get("charged").intValue(), 0);
+    assertEquals(colCons.get("negative").intValue(), 0);
+    assertEquals(colCons.get("polar").intValue(), -1);
+    assertEquals(colCons.get("positive").intValue(), 0);
+    assertEquals(colCons.get("aromatic").intValue(), 0);
+  }
+
+  /**
+   * Test for the case whether the number of non-gapped sequences in a column
+   * has to be above a threshold
+   */
+  @Test(groups = "Functional")
+  public void testCalculate_threshold()
+  {
+    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    seqs.add(new Sequence("seq1", "VGIV-"));
+    seqs.add(new Sequence("seq2", "V-iL-")); // not case sensitive
+    seqs.add(new Sequence("seq3", "V-IW-"));
+    seqs.add(new Sequence("seq4", "VGLH-"));
+    seqs.add(new Sequence("seq5", "VGLH-"));
+  
+    /*
+     * threshold 50% means a residue has to occur 3 or more times
+     * in a column to be counted for conservation
+     */
+    // TODO: ConservationThread uses a value of 3
+    // calculateConservation states it is the minimum number of sequences
+    // but it is treated as percentage threshold in calculate() ?
+    Conservation cons = new Conservation("", 50, seqs, 0, 4);
+    cons.calculate();
+  
+    /*
+     * column 0: all V (hydrophobic/aliphatic/small)
+     */
+    Map<String, Integer> colCons = cons.total[0];
+    assertEquals(colCons.get("hydrophobic").intValue(), 1);
+    assertEquals(colCons.get("aliphatic").intValue(), 1);
+    assertEquals(colCons.get("small").intValue(), 1);
+    assertEquals(colCons.get("tiny").intValue(), 0);
+    assertEquals(colCons.get("proline").intValue(), 0);
+    assertEquals(colCons.get("charged").intValue(), 0);
+    assertEquals(colCons.get("negative").intValue(), 0);
+    assertEquals(colCons.get("polar").intValue(), 0);
+    assertEquals(colCons.get("positive").intValue(), 0);
+    assertEquals(colCons.get("aromatic").intValue(), 0);
+  
+    /*
+     * column 1: all G (hydrophobic/small/tiny)
+     * gaps are ignored as not above threshold
+     */
+    colCons = cons.total[1];
+    assertEquals(colCons.get("hydrophobic").intValue(), 1);
+    assertEquals(colCons.get("aliphatic").intValue(), 0);
+    assertEquals(colCons.get("small").intValue(), 1);
+    assertEquals(colCons.get("tiny").intValue(), 1);
+    assertEquals(colCons.get("proline").intValue(), 0);
+    assertEquals(colCons.get("charged").intValue(), 0);
+    assertEquals(colCons.get("negative").intValue(), 0);
+    assertEquals(colCons.get("polar").intValue(), 0);
+    assertEquals(colCons.get("positive").intValue(), 0);
+    assertEquals(colCons.get("aromatic").intValue(), 0);
+  
+    /*
+     * column 2: I/L (aliphatic/hydrophobic), all others negatively conserved
+     */
+    colCons = cons.total[2];
+    assertEquals(colCons.get("hydrophobic").intValue(), 1);
+    assertEquals(colCons.get("aliphatic").intValue(), 1);
+    assertEquals(colCons.get("small").intValue(), 0);
+    assertEquals(colCons.get("tiny").intValue(), 0);
+    assertEquals(colCons.get("proline").intValue(), 0);
+    assertEquals(colCons.get("charged").intValue(), 0);
+    assertEquals(colCons.get("negative").intValue(), 0);
+    assertEquals(colCons.get("polar").intValue(), 0);
+    assertEquals(colCons.get("positive").intValue(), 0);
+    assertEquals(colCons.get("aromatic").intValue(), 0);
+  
+    /*
+     * column 3: nothing above threshold
+     */
+    colCons = cons.total[3];
+    assertTrue(colCons.isEmpty());
+  
+    /*
+     * column 4: all gaps - counted as having all properties
+     */
+    colCons = cons.total[4];
+    assertEquals(colCons.get("hydrophobic").intValue(), 1);
+    assertEquals(colCons.get("aliphatic").intValue(), 1);
+    assertEquals(colCons.get("small").intValue(), 1);
+    assertEquals(colCons.get("tiny").intValue(), 1);
+    assertEquals(colCons.get("proline").intValue(), 1);
+    assertEquals(colCons.get("charged").intValue(), 1);
+    assertEquals(colCons.get("negative").intValue(), 1);
+    assertEquals(colCons.get("polar").intValue(), 1);
+    assertEquals(colCons.get("positive").intValue(), 1);
+    assertEquals(colCons.get("aromatic").intValue(), 1);
+  }
+
+  /**
+   * Test the method that derives the conservation 'sequence' and the mouseover
+   * tooltips from the computed conservation
+   */
+  @Test(groups = "Functional")
+  public void testVerdict()
+  {
+    List<SequenceI> seqs = new ArrayList<SequenceI>();
+    seqs.add(new Sequence("seq1", "VGIVV-H"));
+    seqs.add(new Sequence("seq2", "VGILL-H"));
+    seqs.add(new Sequence("seq3", "VGIW--R"));
+    seqs.add(new Sequence("seq4", "VGLHH--"));
+    seqs.add(new Sequence("seq5", "VGLHH-R"));
+    seqs.add(new Sequence("seq6", "VGLHH--"));
+    seqs.add(new Sequence("seq7", "VGLHH-R"));
+    seqs.add(new Sequence("seq8", "VGLHH-R"));
+
+    // calculate with no threshold
+    Conservation cons = new Conservation("", 0, seqs, 0, 6);
+    cons.calculate();
+    // positive and negative conservation where <25% gaps in columns
+    cons.verdict(false, 25);
+
+    /*
+     * verify conservation 'sequence'
+     * cols 0 fully conserved and above threshold (*)
+     * col 2 properties fully conserved (+)
+     * col 3 VLWH 1 positively and 3 negatively conserved properties
+     * col 4 has 1 positively conserved property, but because gap contributes a
+     * 'positive' for all properties, no negative conservation is counted
+     * col 5 is all gaps
+     * col 6 has 25% gaps so fails threshold test
+     */
+    assertEquals(cons.getConsSequence().getSequenceAsString(), "**+41--");
+
+    /*
+     * verify tooltips; conserved properties are sorted alphabetically within
+     * positive followed by negative
+     */
+    assertEquals(
+            cons.getTooltip(0),
+            "aliphatic hydrophobic small !aromatic !charged !negative !polar !positive !proline !tiny");
+    assertEquals(
+            cons.getTooltip(1),
+            "hydrophobic small tiny !aliphatic !aromatic !charged !negative !polar !positive !proline");
+    assertEquals(
+            cons.getTooltip(2),
+            "aliphatic hydrophobic !aromatic !charged !negative !polar !positive !proline !small !tiny");
+    assertEquals(cons.getTooltip(3), "hydrophobic !negative !proline !tiny");
+    assertEquals(cons.getTooltip(4), "hydrophobic");
+    assertEquals(cons.getTooltip(5), "");
+    assertEquals(cons.getTooltip(6), "");
+  }
+}
similarity index 96%
rename from test/jalview/analysis/ResidueCountTest.java
rename to test/jalview/datamodel/ResidueCountTest.java
index 4a71f89..f98e3d3 100644 (file)
@@ -1,10 +1,10 @@
-package jalview.analysis;
+package jalview.datamodel;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
-import jalview.analysis.ResidueCount.SymbolCounts;
+import jalview.datamodel.ResidueCount.SymbolCounts;
 
 import org.junit.Assert;
 import org.testng.annotations.Test;
@@ -374,11 +374,19 @@ public class ResidueCountTest
     }
     rc.add('F');
     
+    /*
+     * percentages are rounded (0.5 rounded up)
+     * 10/40 9/40 7/40 6/40 1/40
+     */
     assertEquals(rc.getTooltip(40, 0),
-            "P 25%; W 22%; C 17%; Q 17%; K 15%; F 2%");
+            "P 25%; W 23%; C 18%; Q 18%; K 15%; F 3%");
 
+    rc.add('Q');
+    /*
+     * 10/30 9/30 8/30 7/30 6/30 1/30
+     */
     assertEquals(rc.getTooltip(30, 1),
-            "P 33.3%; W 30.0%; C 23.3%; Q 23.3%; K 20.0%; F 3.3%");
+            "P 33.3%; W 30.0%; Q 26.7%; C 23.3%; K 20.0%; F 3.3%");
   }
 
   @Test(groups = "Functional")
index b4e8629..7b1eac1 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import static jalview.util.UrlConstants.DB_ACCESSION;
+import static jalview.util.UrlConstants.SEQUENCE_ID;
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
@@ -458,11 +460,15 @@ public class PopupMenuTest
     List<DBRefEntry> refs = new ArrayList<DBRefEntry>();
 
     // links as might be added into Preferences | Connections dialog
-    links.add("EMBL-EBI Search | http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_NAME$");
-    links.add("UNIPROT | http://www.uniprot.org/uniprot/$SEQUENCE_ID$");
-    links.add("INTERPRO | http://www.ebi.ac.uk/interpro/entry/$SEQUENCE_ID$");
+    links.add("EMBL-EBI Search | http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$"
+            + SEQUENCE_ID + "$");
+    links.add("UNIPROT | http://www.uniprot.org/uniprot/$" + DB_ACCESSION
+            + "$");
+    links.add("INTERPRO | http://www.ebi.ac.uk/interpro/entry/$"
+            + DB_ACCESSION + "$");
     // Gene3D entry tests for case (in)sensitivity
-    links.add("Gene3D | http://gene3d.biochem.ucl.ac.uk/Gene3D/search?sterm=$SEQUENCE_ID$&mode=protein");
+    links.add("Gene3D | http://gene3d.biochem.ucl.ac.uk/Gene3D/search?sterm=$"
+            + DB_ACCESSION + "$&mode=protein");
 
     // make seq0 dbrefs
     refs.add(new DBRefEntry(DBRefSource.UNIPROT, "1", "P83527"));
index 8173dd1..318ba3f 100644 (file)
@@ -4,7 +4,8 @@ import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.analysis.Profile;
+import jalview.datamodel.Profile;
+import jalview.datamodel.ProfileI;
 
 import java.awt.Color;
 
@@ -22,7 +23,7 @@ public class ResidueColourSchemeTest
      * SR-T
      * SR-T
      */
-    Profile[] profiles = new Profile[4]; 
+    ProfileI[] profiles = new ProfileI[4]; 
     profiles[0] = new Profile(4, 0, 2, "AS");
     profiles[1] = new Profile(4, 0, 4, "R");
     profiles[2] = new Profile(4, 4, 0, "");
index b68ca68..41d7448 100644 (file)
@@ -25,6 +25,7 @@ import static org.testng.AssertJUnit.assertNull;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import org.testng.annotations.Test;
 
@@ -1556,4 +1557,62 @@ public class ResiduePropertiesTest
     assertEquals('Q', ResidueProperties.getSingleCharacterCode("Gln"));
     assertEquals('Q', ResidueProperties.getSingleCharacterCode("gln"));
   }
+
+  @Test(groups = { "Functional" })
+  public void testPhysicoChemicalProperties()
+  {
+    checkProperty("aromatic", "FYWH-*");
+    checkProperty("aliphatic", "IVL-*");
+    checkProperty("tiny", "GAS-*");
+    checkProperty("small", "VCTGACSDNP-*");
+    checkProperty("charged", "HKRDE-*");
+    checkProperty("negative", "DE-*");
+    checkProperty("polar", "YWHRKTSNDEQ-*X");
+    checkProperty("positive", "HKR-*");
+    checkProperty("proline", "P-*");
+    checkProperty("hydrophobic", "MILVFYWHKCGAC-*X");
+  }
+
+  /**
+   * Verify that the residues in the list have the named property, and other
+   * residues do not
+   * 
+   * @param property
+   * @param residues
+   */
+  void checkProperty(String property, String residues)
+  {
+    Map<String, Integer> props = ResidueProperties.propHash.get(property);
+
+    /*
+     * assert residues have the property (value 1 in lookup)
+     */
+    for (char res : residues.toCharArray())
+    {
+      assertEquals(res + " should be " + property, 1,
+              props.get(String.valueOf(res)).intValue());
+    }
+
+    /*
+     * assert other residues do not (value 0 in lookup)
+     */
+    for (String res : ResidueProperties.aa)
+    {
+      if (!residues.contains(res))
+      {
+        Integer propValue = props.get(String.valueOf(res));
+
+        if (propValue != null)
+        {
+          /*
+           * conservation calculation assigns unexpected symbols
+           * the same value as '-'; here we just check those which
+           * explicitly do not have the property
+           */
+          assertEquals(res + " should not be " + property, 0,
+                  propValue.intValue());
+        }
+      }
+    }
+  }
 }
index 8d2901d..1c68a71 100644 (file)
@@ -74,9 +74,16 @@ public class DBRefUtilsTest
     assertSame(ref2, selected[0]);
     assertSame(ref3, selected[1]);
 
-    sources = new String[] { "Uniprot", "EMBLCDS" };
+    sources = new String[] { "EMBLCDS" };
     selected = DBRefUtils.selectRefs(dbrefs, sources);
     assertNull(selected);
+
+    sources = new String[] { "embl", "uniprot" };
+    selected = DBRefUtils.selectRefs(dbrefs, sources);
+    assertEquals(3, selected.length);
+    assertSame(ref1, selected[0]);
+    assertSame(ref2, selected[1]);
+    assertSame(ref3, selected[2]);
   }
 
   /**
index 18199f9..2082963 100644 (file)
@@ -10,23 +10,59 @@ public class FormatTest
   public void testAppendPercentage()
   {
     StringBuilder sb = new StringBuilder();
-    Format.appendPercentage(sb, 123.456f, 0);
+    Format.appendPercentage(sb, 123.436f, 0);
     assertEquals(sb.toString(), "123");
 
     sb.setLength(0);
-    Format.appendPercentage(sb, 123.456f, 1);
+    Format.appendPercentage(sb, 123.536f, 0);
+    assertEquals(sb.toString(), "124");
+
+    sb.setLength(0);
+    Format.appendPercentage(sb, 799.536f, 0);
+    assertEquals(sb.toString(), "800");
+
+    sb.setLength(0);
+    Format.appendPercentage(sb, 123.436f, 1);
     assertEquals(sb.toString(), "123.4");
 
     sb.setLength(0);
-    Format.appendPercentage(sb, 123.456f, 2);
-    assertEquals(sb.toString(), "123.45");
+    Format.appendPercentage(sb, 123.436f, 2);
+    assertEquals(sb.toString(), "123.44");
 
     sb.setLength(0);
-    Format.appendPercentage(sb, 123.456f, 3);
-    assertEquals(sb.toString(), "123.456");
+    Format.appendPercentage(sb, 123.436f, 3);
+    assertEquals(sb.toString(), "123.436");
 
     sb.setLength(0);
-    Format.appendPercentage(sb, 123.456f, 4);
-    assertEquals(sb.toString(), "123.4560");
+    Format.appendPercentage(sb, 123.436f, 4);
+    assertEquals(sb.toString(), "123.4360");
+  }
+
+  @Test(groups = "Functional")
+  public void testForm_float()
+  {
+    Format f = new Format("%3.2f");
+    assertEquals(f.form(123f), "123.00");
+    assertEquals(f.form(123.1f), "123.10");
+    assertEquals(f.form(123.12f), "123.12");
+    assertEquals(f.form(123.124f), "123.12");
+    assertEquals(f.form(123.125f), "123.13");
+    assertEquals(f.form(123.126f), "123.13");
+
+    f = new Format("%3.0f");
+    assertEquals(f.form(123f), "123.");
+    assertEquals(f.form(12f), "12.");
+    assertEquals(f.form(123.4f), "123.");
+    assertEquals(f.form(123.5f), "124.");
+    assertEquals(f.form(123.6f), "124.");
+    assertEquals(f.form(129.6f), "130.");
+  }
+
+  @Test(groups = "Functional")
+  public void testRepeat()
+  {
+    assertEquals(Format.repeat('a', 3), "aaa");
+    assertEquals(Format.repeat('b', 0), "");
+    assertEquals(Format.repeat('c', -1), "");
   }
 }
index 45ef6af..f219fb8 100644 (file)
  */
 package jalview.util;
 
+import static jalview.util.UrlConstants.DB_ACCESSION;
 import static jalview.util.UrlConstants.SEQUENCE_ID;
-import static jalview.util.UrlConstants.SEQUENCE_NAME;
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.Sequence;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
 import org.testng.annotations.Test;
 
 public class UrlLinkTest
@@ -52,39 +61,39 @@ public class UrlLinkTest
   @Test(groups = { "Functional" })
   public void testUrlLinkCreationNoRegex()
   {
-    // SEQUENCE_NAME
-    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_NAME
+    // SEQUENCE_ID
+    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
             + DELIM + URL_SUFFIX);
-    assertEquals(DB.toUpperCase(), ul.getTarget());
+    assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
     assertEquals(URL_PREFIX, ul.getUrl_prefix());
     assertEquals(URL_SUFFIX, ul.getUrl_suffix());
     assertTrue(ul.isDynamic());
-    assertFalse(ul.usesSeqId());
+    assertFalse(ul.usesDBAccession());
     assertNull(ul.getRegexReplace());
     assertTrue(ul.isValid());
     assertNull(ul.getInvalidMessage());
 
-    // SEQUENCE_ID
-    ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID + DELIM
+    // DB_ACCESSION
+    ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + DB_ACCESSION + DELIM
             + URL_SUFFIX);
-    assertEquals(DB.toUpperCase(), ul.getTarget());
+    assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
     assertEquals(URL_PREFIX, ul.getUrl_prefix());
     assertEquals(URL_SUFFIX, ul.getUrl_suffix());
     assertTrue(ul.isDynamic());
-    assertTrue(ul.usesSeqId());
+    assertTrue(ul.usesDBAccession());
     assertNull(ul.getRegexReplace());
     assertTrue(ul.isValid());
     assertNull(ul.getInvalidMessage());
 
     // Not dynamic
     ul = new UrlLink(DB + SEP + URL_PREFIX + URL_SUFFIX.substring(1));
-    assertEquals(DB.toUpperCase(), ul.getTarget());
+    assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
     assertEquals(URL_PREFIX + URL_SUFFIX.substring(1), ul.getUrl_prefix());
     assertFalse(ul.isDynamic());
-    assertFalse(ul.usesSeqId());
+    assertFalse(ul.usesDBAccession());
     assertNull(ul.getRegexReplace());
     assertTrue(ul.isValid());
     assertNull(ul.getInvalidMessage());
@@ -96,43 +105,43 @@ public class UrlLinkTest
   @Test(groups = { "Functional" })
   public void testUrlLinkCreationWithRegex()
   {
-    // SEQUENCE_NAME
-    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_NAME
+    // SEQUENCE_ID
+    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
             + REGEX_NESTED + DELIM + URL_SUFFIX);
-    assertEquals(DB.toUpperCase(), ul.getTarget());
+    assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
     assertEquals(URL_PREFIX, ul.getUrl_prefix());
     assertEquals(URL_SUFFIX, ul.getUrl_suffix());
     assertTrue(ul.isDynamic());
-    assertFalse(ul.usesSeqId());
+    assertFalse(ul.usesDBAccession());
     assertEquals(REGEX_NESTED.substring(2, REGEX_NESTED.length() - 2),
             ul.getRegexReplace());
     assertTrue(ul.isValid());
     assertNull(ul.getInvalidMessage());
 
-    // SEQUENCE_ID
-    ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
+    // DB_ACCESSION
+    ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + DB_ACCESSION
             + REGEX_NESTED + DELIM + URL_SUFFIX);
-    assertEquals(DB.toUpperCase(), ul.getTarget());
+    assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
     assertEquals(URL_PREFIX, ul.getUrl_prefix());
     assertEquals(URL_SUFFIX, ul.getUrl_suffix());
     assertTrue(ul.isDynamic());
-    assertTrue(ul.usesSeqId());
+    assertTrue(ul.usesDBAccession());
     assertEquals(REGEX_NESTED.substring(2, REGEX_NESTED.length() - 2),
             ul.getRegexReplace());
     assertTrue(ul.isValid());
     assertNull(ul.getInvalidMessage());
 
     // invalid regex
-    ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
+    ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + DB_ACCESSION
             + REGEX_RUBBISH + DELIM + URL_SUFFIX);
-    assertEquals(DB.toUpperCase(), ul.getTarget());
+    assertEquals(DB, ul.getTarget());
     assertEquals(DB, ul.getLabel());
     assertEquals(URL_PREFIX, ul.getUrl_prefix());
     assertEquals(URL_SUFFIX, ul.getUrl_suffix());
     assertTrue(ul.isDynamic());
-    assertTrue(ul.usesSeqId());
+    assertTrue(ul.usesDBAccession());
     assertEquals(REGEX_RUBBISH.substring(2, REGEX_RUBBISH.length() - 2),
             ul.getRegexReplace());
     assertFalse(ul.isValid());
@@ -150,7 +159,7 @@ public class UrlLinkTest
   public void testMakeUrlNoRegex()
   {
     // Single non-regex
-    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_NAME
+    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
             + DELIM + URL_SUFFIX);
     String idstring = "FER_CAPAA";
     String[] urls = ul.makeUrls(idstring, true);
@@ -167,13 +176,14 @@ public class UrlLinkTest
   }
 
   /**
-   * Test construction of link by substituting sequence id or name
+   * Test construction of link by substituting sequence id or name using regular
+   * expression
    */
   @Test(groups = { "Functional" })
   public void testMakeUrlWithRegex()
   {
     // Unused regex
-    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
+    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + DB_ACCESSION
             + REGEX_NESTED + DELIM + URL_SUFFIX);
     String idstring = "FER_CAPAA";
     String[] urls = ul.makeUrls(idstring, true);
@@ -243,4 +253,150 @@ public class UrlLinkTest
     assertNull(ul.getInvalidMessage());
   }
 
+  /**
+   * Test creating links with null sequence
+   */
+  @Test(groups = { "Functional" })
+  public void testCreateLinksFromNullSequence()
+  {
+    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
+            + DELIM + URL_SUFFIX);
+
+    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+    ul.createLinksFromSeq(null, linkset);
+
+    String key = DB + SEP + URL_PREFIX;
+    assertEquals(1, linkset.size());
+    assertTrue(linkset.containsKey(key));
+    assertEquals(linkset.get(key).get(0), DB);
+    assertEquals(linkset.get(key).get(1), DB);
+    assertEquals(linkset.get(key).get(2), null);
+    assertEquals(linkset.get(key).get(3), URL_PREFIX);
+  }
+
+  /**
+   * Test creating links with non-dynamic urlLink
+   */
+  @Test(groups = { "Functional" })
+  public void testCreateLinksForNonDynamic()
+  {
+    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + URL_SUFFIX);
+
+    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+    ul.createLinksFromSeq(null, linkset);
+
+    String key = DB + SEP + URL_PREFIX + URL_SUFFIX;
+    assertEquals(1, linkset.size());
+    assertTrue(linkset.containsKey(key));
+    assertEquals(linkset.get(key).get(0), DB);
+    assertEquals(linkset.get(key).get(1), DB);
+    assertEquals(linkset.get(key).get(2), null);
+    assertEquals(linkset.get(key).get(3), URL_PREFIX + URL_SUFFIX);
+  }
+
+  /**
+   * Test creating links
+   */
+  @Test(groups = { "Functional" })
+  public void testCreateLinksFromSequence()
+  {
+
+    // create list of links and list of DBRefs
+    List<String> links = new ArrayList<String>();
+    List<DBRefEntry> refs = new ArrayList<DBRefEntry>();
+
+    // links as might be added into Preferences | Connections dialog
+    links.add("EMBL-EBI Search | http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$"
+            + SEQUENCE_ID + "$");
+    links.add("UNIPROT | http://www.uniprot.org/uniprot/$" + DB_ACCESSION
+            + "$");
+    links.add("INTERPRO | http://www.ebi.ac.uk/interpro/entry/$"
+            + DB_ACCESSION + "$");
+
+    // make seq0 dbrefs
+    refs.add(new DBRefEntry(DBRefSource.UNIPROT, "1", "P83527"));
+    refs.add(new DBRefEntry("INTERPRO", "1", "IPR001041"));
+    refs.add(new DBRefEntry("INTERPRO", "1", "IPR006058"));
+    refs.add(new DBRefEntry("INTERPRO", "1", "IPR012675"));
+
+    Sequence seq0 = new Sequence("FER1", "AKPNGVL");
+
+    // add all the dbrefs to the sequence
+    seq0.addDBRef(refs.get(0));
+    seq0.addDBRef(refs.get(1));
+    seq0.addDBRef(refs.get(2));
+    seq0.addDBRef(refs.get(3));
+    seq0.createDatasetSequence();
+
+    // Test where link takes a sequence id as replacement
+    UrlLink ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + SEQUENCE_ID
+            + DELIM + URL_SUFFIX);
+
+    Map<String, List<String>> linkset = new LinkedHashMap<String, List<String>>();
+    ul.createLinksFromSeq(seq0, linkset);
+
+    String key = seq0.getName() + SEP + URL_PREFIX + seq0.getName()
+            + URL_SUFFIX;
+    assertEquals(1, linkset.size());
+    assertTrue(linkset.containsKey(key));
+    assertEquals(linkset.get(key).get(0), DB);
+    assertEquals(linkset.get(key).get(1), DB);
+    assertEquals(linkset.get(key).get(2), seq0.getName());
+    assertEquals(linkset.get(key).get(3), URL_PREFIX + seq0.getName()
+            + URL_SUFFIX);
+
+    // Test where link takes a db annotation id and only has one dbref
+    ul = new UrlLink(links.get(1));
+    linkset = new LinkedHashMap<String, List<String>>();
+    ul.createLinksFromSeq(seq0, linkset);
+
+    key = "P83527|http://www.uniprot.org/uniprot/P83527";
+    assertEquals(1, linkset.size());
+    assertTrue(linkset.containsKey(key));
+    assertEquals(linkset.get(key).get(0), DBRefSource.UNIPROT);
+    assertEquals(linkset.get(key).get(1), DBRefSource.UNIPROT + SEP
+            + "P83527");
+    assertEquals(linkset.get(key).get(2), "P83527");
+    assertEquals(linkset.get(key).get(3),
+            "http://www.uniprot.org/uniprot/P83527");
+
+    // Test where link takes a db annotation id and has multiple dbrefs
+    ul = new UrlLink(links.get(2));
+    linkset = new LinkedHashMap<String, List<String>>();
+    ul.createLinksFromSeq(seq0, linkset);
+    assertEquals(3, linkset.size());
+
+    // check each link made it in correctly
+    key = "IPR001041|http://www.ebi.ac.uk/interpro/entry/IPR001041";
+    assertTrue(linkset.containsKey(key));
+    assertEquals(linkset.get(key).get(0), "INTERPRO");
+    assertEquals(linkset.get(key).get(1), "INTERPRO" + SEP + "IPR001041");
+    assertEquals(linkset.get(key).get(2), "IPR001041");
+    assertEquals(linkset.get(key).get(3),
+            "http://www.ebi.ac.uk/interpro/entry/IPR001041");
+
+    key = "IPR006058|http://www.ebi.ac.uk/interpro/entry/IPR006058";
+    assertTrue(linkset.containsKey(key));
+    assertEquals(linkset.get(key).get(0), "INTERPRO");
+    assertEquals(linkset.get(key).get(1), "INTERPRO" + SEP + "IPR006058");
+    assertEquals(linkset.get(key).get(2), "IPR006058");
+    assertEquals(linkset.get(key).get(3),
+            "http://www.ebi.ac.uk/interpro/entry/IPR006058");
+
+    key = "IPR012675|http://www.ebi.ac.uk/interpro/entry/IPR012675";
+    assertTrue(linkset.containsKey(key));
+    assertEquals(linkset.get(key).get(0), "INTERPRO");
+    assertEquals(linkset.get(key).get(1), "INTERPRO" + SEP + "IPR012675");
+    assertEquals(linkset.get(key).get(2), "IPR012675");
+    assertEquals(linkset.get(key).get(3),
+            "http://www.ebi.ac.uk/interpro/entry/IPR012675");
+
+    // Test where there are no matching dbrefs for the link
+    ul = new UrlLink(DB + SEP + URL_PREFIX + DELIM + DB_ACCESSION + DELIM
+            + URL_SUFFIX);
+    linkset = new LinkedHashMap<String, List<String>>();
+    ul.createLinksFromSeq(seq0, linkset);
+    assertTrue(linkset.isEmpty());
+  }
+
 }