Merge branch 'features/JAL-2388OverviewWindow' into develop
authorJim Procter <jprocter@issues.jalview.org>
Fri, 12 May 2017 14:09:45 +0000 (15:09 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Fri, 12 May 2017 14:09:45 +0000 (15:09 +0100)
24 files changed:
examples/groovy/featureCounter.groovy [deleted file]
examples/groovy/featuresCounter.groovy [new file with mode: 0644]
examples/groovy/multipleFeatureAnnotations.groovy [deleted file]
examples/groovy/visibleFeaturesCounter.groovy [new file with mode: 0644]
help/help.jhm
help/helpTOC.xml
help/html/features/groovy.html
help/html/groovy/featureCounter.html [deleted file]
help/html/groovy/featuresCounter.html [new file with mode: 0644]
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/appletgui/IdPanel.java
src/jalview/appletgui/SequenceRenderer.java
src/jalview/datamodel/Annotation.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/CalculationChooser.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceRenderer.java
src/jalview/io/StockholmFile.java
src/jalview/workers/AlignmentAnnotationFactory.java
src/jalview/workers/ColumnCounterSetWorker.java [moved from src/jalview/workers/ColumnCounterWorker.java with 61% similarity]
src/jalview/workers/FeatureSetCounterI.java [moved from src/jalview/workers/FeatureCounterI.java with 76% similarity]
test/jalview/gui/SequenceRendererTest.java
test/jalview/io/StockholmFileTest.java

diff --git a/examples/groovy/featureCounter.groovy b/examples/groovy/featureCounter.groovy
deleted file mode 100644 (file)
index 9059dd0..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- */
-import jalview.workers.FeatureCounterI;
-import jalview.workers.AlignmentAnnotationFactory;
-
-/*
- * Example script that registers two alignment annotation calculators
- * - one that counts residues in a column with Pfam annotation
- * - one that counts only charged residues with Pfam annotation
- *
- * To try:
- * 1. load uniref50.fa from the examples folder
- * 2. load features onto it from from examples/exampleFeatures.txt
- * 3. Open this script in the Groovy console.
- * 4. Either execute this script from the console, or via Calculate->Run Groovy Script
- * To explore further, try changing this script to count other kinds of occurrences of 
- * residue and sequence features at columns in an alignment.
- */
-
-/*
- * A closure that returns true for any Charged residue
- */
-def isCharged = { residue ->
-    switch(residue) {
-        case ['D', 'd', 'E', 'e', 'H', 'h', 'K', 'k', 'R', 'r']:
-            return true
-    }
-    false
-} 
-
-/*
- * A closure that returns 1 if sequence features include type 'Pfam', else 0
- * Argument should be a list of SequenceFeature 
- */
-def hasPfam = { features -> 
-    for (sf in features)
-    {
-        /*
-         * Here we inspect the type of the sequence feature.
-         * You can also test sf.description, sf.score, sf.featureGroup,
-         * sf.strand, sf.phase, sf.begin, sf.end
-         * or sf.getValue(attributeName) for GFF 'column 9' properties
-         */
-        if ("Pfam".equals(sf.type))
-        {
-            return true
-        }
-    }
-    false
-}
-
-/*
- * Closure that computes an annotation based on 
- * presence of particular residues and features
- * Parameters are
- * - the name (label) for the alignment annotation
- * - the description (tooltip) for the annotation
- * - a closure (groovy function) that tests whether to include a residue
- * - a closure that tests whether to increment count based on sequence features  
- */
-def getColumnCounter = { name, desc, acceptResidue, acceptFeatures ->
-    [
-     getName: { name }, 
-     getDescription: { desc },
-     getMinColour: { [0, 255, 255] }, // cyan
-     getMaxColour: { [0, 0, 255] }, // blue
-     count: 
-         { res, feats -> 
-            def c = 0
-            if (acceptResidue.call(res))
-            {
-                if (acceptFeatures.call(feats))
-                {
-                    c++
-                }
-            }
-            c
-         }
-     ] as FeatureCounterI
-}
-
-/*
- * Define an annotation row that counts any residue with Pfam domain annotation
- */
-def pfamAnnotation = getColumnCounter("Pfam", "Count of residues with Pfam domain annotation", {true}, hasPfam)
-
-/*
- * Define an annotation row that counts charged residues with Pfam domain annotation
- */
-def chargedPfamAnnotation = getColumnCounter("Pfam charged", "Count of charged residues with Pfam domain annotation", isCharged, hasPfam)
-
-/*
- * Register the annotations
- */
-AlignmentAnnotationFactory.newCalculator(pfamAnnotation) 
-AlignmentAnnotationFactory.newCalculator(chargedPfamAnnotation)
diff --git a/examples/groovy/featuresCounter.groovy b/examples/groovy/featuresCounter.groovy
new file mode 100644 (file)
index 0000000..dc4c97c
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+import jalview.workers.AlignmentAnnotationFactory;
+import jalview.workers.FeatureSetCounterI;
+
+/*
+ * Example script to compute two alignment annotations
+ * - count of Phosphorylation features
+ * - count of Turn features
+ * To try this, first load example file uniref50.fa and load on features file
+ * exampleFeatures.txt, before running this script
+ *
+ * The script only needs to be run once - it will be registered by Jalview
+ * and recalculated automatically when the alignment changes.
+ * 
+ * Note: The feature api provided by 2.10.2 is not compatible with scripts
+ * that worked with earlier Jalview versions. Apologies for the inconvenience.
+ */
+def annotator = 
+    [
+     getNames: { ['Phosphorylation', 'Turn'] as String[] }, 
+     getDescriptions:  { ['Count of Phosphorylation features', 'Count of Turn features'] as String[] },
+     getMinColour: { [0, 255, 255] as int[] }, // cyan
+     getMaxColour: { [0, 0, 255] as int[] }, // blue
+     count: 
+         { res, feats -> 
+                int phos
+                int turn
+                for (sf in feats)
+                {
+                         /*
+                          * Here we inspect the type of the sequence feature.
+                          * You can also test sf.description, sf.score, sf.featureGroup,
+                          * sf.strand, sf.phase, sf.begin, sf.end
+                          * or sf.getValue(attributeName) for GFF 'column 9' properties
+                          */
+                          if (sf.type.contains('TURN'))
+                   {
+                      turn++
+                   }
+                   if (sf.type.contains('PHOSPHORYLATION'))
+                   {
+                      phos++
+                   }
+                }
+                [phos, turn] as int[]
+         }
+     ] as FeatureSetCounterI
+    
+/*
+ * Register the annotation calculator with Jalview
+ */
+AlignmentAnnotationFactory.newCalculator(annotator) 
diff --git a/examples/groovy/multipleFeatureAnnotations.groovy b/examples/groovy/multipleFeatureAnnotations.groovy
deleted file mode 100644 (file)
index 592c7f5..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-import jalview.workers.AlignmentAnnotationFactory;
-import jalview.workers.AnnotationProviderI;
-import jalview.datamodel.AlignmentAnnotation;
-import jalview.datamodel.Annotation;
-import jalview.util.ColorUtils;
-import jalview.util.Comparison;
-import java.awt.Color;
-
-/*
- * Example script to compute two alignment annotations
- * - count of Phosphorylation features
- * - count of Turn features
- * To try this, first load example file uniref50.fa and load on features file
- * exampleFeatures.txt, before running this script
- *
- * The script only needs to be run once - it will be registered by Jalview
- * and recalculated automatically when the alignment changes.
- */
-
-/*
- * A closure that returns true if value includes "PHOSPHORYLATION"
- */
-def phosCounter = { type ->    type.contains("PHOSPHORYLATION") }
-
-/*
- * A closure that returns true if value includes "TURN"
- */
-def turnCounter = { type ->    type.contains("TURN") }
-
-/*
- * A closure that computes and returns an array of Annotation values,
- * one for each column of the alignment
- */
-def getAnnotations(al, fr, counter) 
-{
-    def width = al.width
-    def counts = new int[width] 
-    def max = 0
-    
-    /*
-     * count features in each column, record the maximum value
-     */
-    for (col = 0 ; col < width ; col++)
-    {
-        def count = 0
-        for (row = 0 ; row < al.height ; row++)
-        {
-            seq = al.getSequenceAt(row)
-            if (seq != null && col < seq.getLength())
-            {
-                def res = seq.getCharAt(col)
-                if (!Comparison.isGap(res))
-                {
-                    pos = seq.findPosition(col)
-                    features = fr.findFeaturesAtRes(seq, pos)
-                    for (feature in features)
-                    {
-                        if (counter.call(feature.type))
-                        {
-                            count++
-                        }
-                    }
-                }
-            }
-        }
-        counts[col] = count
-        if (count > max)
-        {
-            max = count
-        }
-    }
-    
-    /*
-     * make the Annotation objects, with a graduated colour scale 
-     * (from min value to max value) for the histogram bars 
-     */
-    def zero = '0' as char
-    def anns = new Annotation[width] 
-    for (col = 0 ; col < width ; col++)
-    {
-        def c = counts[col]
-        if (c > 0)
-        {
-            Color color = ColorUtils.getGraduatedColour(c, 0, Color.cyan,
-                max, Color.blue)
-            anns[col] = AlignmentAnnotationFactory.newAnnotation(String.valueOf(c),
-                String.valueOf(c), zero, c, color)
-        }
-    }
-    anns
-}
-
-/*
- * Define the method that performs the calculations, and builds two
- * AlignmentAnnotation objects
- */
-def annotator = 
-    [ calculateAnnotation: { al, fr ->
-        def phosAnns = getAnnotations(al, fr, phosCounter)
-        def ann1 = AlignmentAnnotationFactory.newAlignmentAnnotation("Phosphorylation", "Count of Phosphorylation features", phosAnns)
-        def turnAnns = getAnnotations(al, fr, turnCounter)
-        def ann2 = AlignmentAnnotationFactory.newAlignmentAnnotation("Turn", "Count of Turn features", turnAnns)
-        return [ann1, ann2]
-      } 
-    ] as AnnotationProviderI
-    
-/*
- * Register the annotation calculator with Jalview
- */
-AlignmentAnnotationFactory.newCalculator(annotator) 
diff --git a/examples/groovy/visibleFeaturesCounter.groovy b/examples/groovy/visibleFeaturesCounter.groovy
new file mode 100644 (file)
index 0000000..b3180f8
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+import jalview.bin.Jalview
+import jalview.workers.FeatureSetCounterI
+import jalview.workers.AlignmentAnnotationFactory
+
+/*
+ * Demonstration of FeatureSetCounterI
+ * compute annotation tracks counting number of displayed 
+ * features of each type in each column
+ */
+
+/*
+ * discover features on the current view
+ */
+def featuresDisp=Jalview.currentAlignFrame.currentView.featuresDisplayed
+if (featuresDisp == null) {
+    print 'Need at least one feature visible on alignment'
+}
+def visibleFeatures=featuresDisp.visibleFeatures.toList()
+assert 'java.util.ArrayList' == visibleFeatures.class.name
+
+/*
+ * A closure that returns an array of features present 
+ * for each feature type in visibleFeatures
+ * Argument 'features' will be a list of SequenceFeature 
+ */
+def getCounts = 
+    { features -> 
+        int[] obs = new int[visibleFeatures.size]
+        for (sf in features)
+        {
+            /*
+             * Here we inspect the type of the sequence feature.
+             * You can also test sf.description, sf.score, sf.featureGroup,
+             * sf.strand, sf.phase, sf.begin, sf.end
+             * or sf.getValue(attributeName) for GFF 'column 9' properties
+             */
+            int pos = 0
+            for (type in visibleFeatures) 
+            {
+              if (type.equals(sf.type)) 
+              {
+                  obs[pos]++
+              }
+              pos++
+            }
+        }
+        obs
+}
+  
+/*
+ * Define something that counts each visible feature type
+ */
+def columnSetCounter =
+    [
+     getNames: { visibleFeatures as String[] }, 
+     getDescriptions:  { visibleFeatures as String[] },
+     getMinColour: { [0, 255, 255] as int[] }, // cyan
+     getMaxColour: { [0, 0, 255] as int[] }, // blue
+     count: 
+         { res, feats -> 
+             getCounts.call(feats) 
+         }
+     ] as FeatureSetCounterI
+
+/*
+ * and register the counter
+ */
+AlignmentAnnotationFactory.newCalculator(columnSetCounter)
index f931e7e..c6ce57d 100755 (executable)
    
    <mapID target="memory" url="html/memory.html" />
    <mapID target="groovy" url="html/features/groovy.html" />
-   <mapID target="groovy.featurecounter" url="html/groovy/featureCounter.html" />
+   <mapID target="groovy.featurescounter" url="html/groovy/featuresCounter.html" />
    <mapID target="privacy" url="html/privacy.html" />
    <mapID target="vamsas" url="html/vamsas/index.html"/>
    <mapID target="aminoAcids" url="html/misc/aminoAcids.html" />
index 989d70b..f76da16 100755 (executable)
@@ -24,6 +24,7 @@
        <tocitem text="Jalview Documentation" target="home" expand="true">
                        <tocitem text="What's new" target="new" expand="true">
                                <tocitem text="Latest Release Notes" target="release"/>
+                               <tocitem text="Groovy Features Counter example" target="groovy.featurescounter"/>
                                <tocitem text="Omit hidden regions in Overview" target="overview"/>
                </tocitem>
                
                <tocitem text="Preferences" target="preferences" />
                <tocitem text="Memory Settings" target="memory" expand="false"/>
                <tocitem text="Scripting with Groovy" target="groovy">
-                       <tocitem text="Groovy Feature Counter example" target="groovy.featurecounter"/>
+                       <tocitem text="Groovy Features Counter example" target="groovy.featurescounter"/>
                </tocitem>
                <tocitem text="Command Line" target="commandline" expand="false">
                        <tocitem text="Command Line Arguments" target="clarguments" />
index 254f92e..d9bf76e 100644 (file)
@@ -108,7 +108,7 @@ print currentAlFrame.getTitle();</pre>
     simplified the alignment analysis programming interface in Jalview
     2.10 to make it easy for you to add your own dynamic annotation
     tracks with Groovy. Have a look at the <a
-      href="../groovy/featureCounter.html">featureCounter.groovy</a>
+      href="../groovy/featuresCounter.html">featuresCounter.groovy</a>
     example for more information.
   </p>
 
diff --git a/help/html/groovy/featureCounter.html b/help/html/groovy/featureCounter.html
deleted file mode 100644 (file)
index 2ebaf45..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-<html>
-<!--
- * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
- * Copyright (C) $$Year-Rel$$ The Jalview Authors
- * 
- * This file is part of Jalview.
- * 
- * Jalview is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License 
- * as published by the Free Software Foundation, either version 3
- * of the License, or (at your option) any later version.
- *  
- * Jalview is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty 
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
- * PURPOSE.  See the GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
- * The Jalview Authors are detailed in the 'AUTHORS' file.
- -->
-<head>
-<title>Extending Jalview with Groovy - Feature Counter Example</title>
-</head>
-<body>
-  <p>
-    <strong>Extending Jalview with Groovy - A customisable
-      feature counter</strong><br /> <br />The groovy script below shows how to
-    add a new calculation track to a Jalview alignment window.
-  </p>
-  <p>As currently written, it will add two tracks to a protein
-    alignment view which count Pfam features in each column, and ones
-    where a charge residue also occur.</p>
-  <p>To try it for yourself:</p>
-  <ol>
-    <li>Copy and paste it into the groovy script console</li>
-    <li>Load the example Feredoxin project (the one that opens by
-      default when you first launched Jalview)</li>
-    <li>Select <strong>Calculations&#8594;Execute Groovy
-        Script</strong> from the alignment window's menu bar to run the script on
-      the current view.
-    </li>
-  </ol>
-  <em><a
-    href="http://www.jalview.org/examples/groovy/featureCounter.groovy">http://www.jalview.org/examples/groovy/featureCounter.groovy</a>
-    - rendered with <a href="http://hilite.me">hilite.me</a></em>
-  <!-- HTML generated using hilite.me -->
-  <div
-    style="background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;">
-    <pre style="margin: 0; line-height: 125%">
-<span style="color: #888888">/*</span>
-<span style="color: #888888"> * Jalview - A Sequence Alignment Editor and Viewer (Version 2.10)</span>
-<span style="color: #888888"> * Copyright (C) 2016 The Jalview Authors</span>
-<span style="color: #888888"> * </span>
-<span style="color: #888888"> * This file is part of Jalview.</span>
-<span style="color: #888888"> * </span>
-<span style="color: #888888"> * Jalview is free software: you can redistribute it and/or</span>
-<span style="color: #888888"> * modify it under the terms of the GNU General Public License </span>
-<span style="color: #888888"> * as published by the Free Software Foundation, either version 3</span>
-<span style="color: #888888"> * of the License, or (at your option) any later version.</span>
-<span style="color: #888888"> *  </span>
-<span style="color: #888888"> * Jalview is distributed in the hope that it will be useful, but </span>
-<span style="color: #888888"> * WITHOUT ANY WARRANTY; without even the implied warranty </span>
-<span style="color: #888888"> * of MERCHANTABILITY or FITNESS FOR A PARTICULAR </span>
-<span style="color: #888888"> * PURPOSE.  See the GNU General Public License for more details.</span>
-<span style="color: #888888"> * </span>
-<span style="color: #888888"> * You should have received a copy of the GNU General Public License</span>
-<span style="color: #888888"> * along with Jalview.  If not, see &lt;http://www.gnu.org/licenses/&gt;.</span>
-<span style="color: #888888"> * The Jalview Authors are detailed in the &#39;AUTHORS&#39; file.</span>
-<span style="color: #888888"> */</span>
-<span style="color: #008800; font-weight: bold">import</span> <span
-        style="color: #0e84b5; font-weight: bold">jalview.workers.FeatureCounterI</span><span
-        style="color: #333333">;</span>
-<span style="color: #008800; font-weight: bold">import</span> <span
-        style="color: #0e84b5; font-weight: bold">jalview.workers.AlignmentAnnotationFactory</span><span
-        style="color: #333333">;</span>
-
-<span style="color: #888888">/*</span>
-<span style="color: #888888"> * Example script that registers two alignment annotation calculators</span>
-<span style="color: #888888"> * - one that counts residues in a column with Pfam annotation</span>
-<span style="color: #888888"> * - one that counts only charged residues with Pfam annotation</span>
-<span style="color: #888888"> *</span>
-<span style="color: #888888"> * To try:</span>
-<span style="color: #888888"> * 1. load uniref50.fa from the examples folder</span>
-<span style="color: #888888"> * 2. load features onto it from from examples/exampleFeatures.txt</span>
-<span style="color: #888888"> * 3. Open this script in the Groovy console.</span>
-<span style="color: #888888"> * 4. Either execute this script from the console, or via Calculate-&gt;Run Groovy Script</span>
-<span style="color: #888888"> </span>
-<span style="color: #888888"> * To explore further, try changing this script to count other kinds of occurrences of </span>
-<span style="color: #888888"> * residue and sequence features at columns in an alignment.</span>
-<span style="color: #888888"> */</span>
-
-<span style="color: #888888">/*</span>
-<span style="color: #888888"> * A closure that returns true for any Charged residue</span>
-<span style="color: #888888"> */</span>
-<span style="color: #333399; font-weight: bold">def</span> isCharged <span
-        style="color: #333333">=</span> <span style="color: #333333">{</span> residue <span
-        style="color: #333333">-&gt;</span>
-    <span style="color: #008800; font-weight: bold">switch</span><span
-        style="color: #333333">(</span>residue<span
-        style="color: #333333">)</span> <span style="color: #333333">{</span>
-        <span style="color: #008800; font-weight: bold">case</span> <span
-        style="color: #333333">[</span><span
-        style="background-color: #fff0f0">&#39;D&#39;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&#39;d&#39;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&#39;E&#39;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&#39;e&#39;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&#39;H&#39;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&#39;h&#39;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&#39;K&#39;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&#39;k&#39;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&#39;R&#39;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&#39;r&#39;</span><span
-        style="color: #333333">]:</span>
-            <span style="color: #008800; font-weight: bold">return</span> <span
-        style="color: #008800; font-weight: bold">true</span>
-    <span style="color: #333333">}</span>
-    <span style="color: #008800; font-weight: bold">false</span>
-<span style="color: #333333">}</span> 
-
-<span style="color: #888888">/*</span>
-<span style="color: #888888"> * A closure that returns 1 if sequence features include type &#39;Pfam&#39;, else 0</span>
-<span style="color: #888888"> * Argument should be a list of SequenceFeature </span>
-<span style="color: #888888"> */</span>
-<span style="color: #333399; font-weight: bold">def</span> hasPfam <span
-        style="color: #333333">=</span> <span style="color: #333333">{</span> features <span
-        style="color: #333333">-&gt;</span> 
-    <span style="color: #008800; font-weight: bold">for</span> <span
-        style="color: #333333">(</span>sf <span
-        style="color: #008800; font-weight: bold">in</span> features<span
-        style="color: #333333">)</span>
-    <span style="color: #333333">{</span>
-        <span style="color: #888888">/*</span>
-<span style="color: #888888">         * Here we inspect the type of the sequence feature.</span>
-<span style="color: #888888">         * You can also test sf.description, sf.score, sf.featureGroup,</span>
-<span style="color: #888888">         * sf.strand, sf.phase, sf.begin, sf.end</span>
-<span style="color: #888888">         * or sf.getValue(attributeName) for GFF &#39;column 9&#39; properties</span>
-<span style="color: #888888">         */</span>
-        <span style="color: #008800; font-weight: bold">if</span> <span
-        style="color: #333333">(</span><span
-        style="background-color: #fff0f0">&quot;Pfam&quot;</span><span
-        style="color: #333333">.</span><span style="color: #0000CC">equals</span><span
-        style="color: #333333">(</span>sf<span style="color: #333333">.</span><span
-        style="color: #0000CC">type</span><span style="color: #333333">))</span>
-        <span style="color: #333333">{</span>
-            <span style="color: #008800; font-weight: bold">return</span> <span
-        style="color: #008800; font-weight: bold">true</span>
-        <span style="color: #333333">}</span>
-    <span style="color: #333333">}</span>
-    <span style="color: #008800; font-weight: bold">false</span>
-<span style="color: #333333">}</span>
-
-<span style="color: #888888">/*</span>
-<span style="color: #888888"> * Closure that computes an annotation based on </span>
-<span style="color: #888888"> * presence of particular residues and features</span>
-<span style="color: #888888"> * Parameters are</span>
-<span style="color: #888888"> * - the name (label) for the alignment annotation</span>
-<span style="color: #888888"> * - the description (tooltip) for the annotation</span>
-<span style="color: #888888"> * - a closure (groovy function) that tests whether to include a residue</span>
-<span style="color: #888888"> * - a closure that tests whether to increment count based on sequence features  </span>
-<span style="color: #888888"> */</span>
-<span style="color: #333399; font-weight: bold">def</span> getColumnCounter <span
-        style="color: #333333">=</span> <span style="color: #333333">{</span> name<span
-        style="color: #333333">,</span> desc<span style="color: #333333">,</span> acceptResidue<span
-        style="color: #333333">,</span> acceptFeatures <span
-        style="color: #333333">-&gt;</span>
-    <span style="color: #333333">[</span>
-     <span style="color: #997700; font-weight: bold">getName:</span> <span
-        style="color: #333333">{</span> name <span
-        style="color: #333333">},</span> 
-     <span style="color: #997700; font-weight: bold">getDescription:</span> <span
-        style="color: #333333">{</span> desc <span
-        style="color: #333333">},</span>
-     <span style="color: #997700; font-weight: bold">getMinColour:</span> <span
-        style="color: #333333">{</span> <span style="color: #333333">[</span><span
-        style="color: #0000DD; font-weight: bold">0</span><span
-        style="color: #333333">,</span> <span
-        style="color: #0000DD; font-weight: bold">255</span><span
-        style="color: #333333">,</span> <span
-        style="color: #0000DD; font-weight: bold">255</span><span
-        style="color: #333333">]</span> <span style="color: #333333">},</span> <span
-        style="color: #888888">// cyan</span>
-     <span style="color: #997700; font-weight: bold">getMaxColour:</span> <span
-        style="color: #333333">{</span> <span style="color: #333333">[</span><span
-        style="color: #0000DD; font-weight: bold">0</span><span
-        style="color: #333333">,</span> <span
-        style="color: #0000DD; font-weight: bold">0</span><span
-        style="color: #333333">,</span> <span
-        style="color: #0000DD; font-weight: bold">255</span><span
-        style="color: #333333">]</span> <span style="color: #333333">},</span> <span
-        style="color: #888888">// blue</span>
-     <span style="color: #997700; font-weight: bold">count:</span> 
-         <span style="color: #333333">{</span> res<span
-        style="color: #333333">,</span> feats <span
-        style="color: #333333">-&gt;</span> 
-            <span style="color: #333399; font-weight: bold">def</span> c <span
-        style="color: #333333">=</span> <span
-        style="color: #0000DD; font-weight: bold">0</span>
-            <span style="color: #008800; font-weight: bold">if</span> <span
-        style="color: #333333">(</span>acceptResidue<span
-        style="color: #333333">.</span><span style="color: #0000CC">call</span><span
-        style="color: #333333">(</span>res<span style="color: #333333">))</span>
-            <span style="color: #333333">{</span>
-                <span style="color: #008800; font-weight: bold">if</span> <span
-        style="color: #333333">(</span>acceptFeatures<span
-        style="color: #333333">.</span><span style="color: #0000CC">call</span><span
-        style="color: #333333">(</span>feats<span style="color: #333333">))</span>
-                <span style="color: #333333">{</span>
-                    c<span style="color: #333333">++</span>
-                <span style="color: #333333">}</span>
-            <span style="color: #333333">}</span>
-            c
-         <span style="color: #333333">}</span>
-     <span style="color: #333333">]</span> <span
-        style="color: #008800; font-weight: bold">as</span> FeatureCounterI
-<span style="color: #333333">}</span>
-
-<span style="color: #888888">/*</span>
-<span style="color: #888888"> * Define an annotation row that counts any residue with Pfam domain annotation</span>
-<span style="color: #888888"> */</span>
-<span style="color: #333399; font-weight: bold">def</span> pfamAnnotation <span
-        style="color: #333333">=</span> getColumnCounter<span
-        style="color: #333333">(</span><span
-        style="background-color: #fff0f0">&quot;Pfam&quot;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&quot;Count of residues with Pfam domain annotation&quot;</span><span
-        style="color: #333333">,</span> <span style="color: #333333">{</span><span
-        style="color: #008800; font-weight: bold">true</span><span
-        style="color: #333333">},</span> hasPfam<span
-        style="color: #333333">)</span>
-
-<span style="color: #888888">/*</span>
-<span style="color: #888888"> * Define an annotation row that counts charged residues with Pfam domain annotation</span>
-<span style="color: #888888"> */</span>
-<span style="color: #333399; font-weight: bold">def</span> chargedPfamAnnotation <span
-        style="color: #333333">=</span> getColumnCounter<span
-        style="color: #333333">(</span><span
-        style="background-color: #fff0f0">&quot;Pfam charged&quot;</span><span
-        style="color: #333333">,</span> <span
-        style="background-color: #fff0f0">&quot;Count of charged residues with Pfam domain annotation&quot;</span><span
-        style="color: #333333">,</span> isCharged<span
-        style="color: #333333">,</span> hasPfam<span
-        style="color: #333333">)</span>
-
-<span style="color: #888888">/*</span>
-<span style="color: #888888"> * Register the annotations</span>
-<span style="color: #888888"> */</span>
-AlignmentAnnotationFactory<span style="color: #333333">.</span><span
-        style="color: #0000CC">newCalculator</span><span
-        style="color: #333333">(</span>pfamAnnotation<span
-        style="color: #333333">)</span> 
-AlignmentAnnotationFactory<span style="color: #333333">.</span><span
-        style="color: #0000CC">newCalculator</span><span
-        style="color: #333333">(</span>chargedPfamAnnotation<span
-        style="color: #333333">)</span>
-</pre>
-  </div>
-</body>
-</html>
diff --git a/help/html/groovy/featuresCounter.html b/help/html/groovy/featuresCounter.html
new file mode 100644 (file)
index 0000000..3b6705b
--- /dev/null
@@ -0,0 +1,123 @@
+<html>
+<!--
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ -->
+<head>
+<title>Extending Jalview with Groovy - Feature Counter Example</title>
+</head>
+<body>
+  <p>
+    <strong>Extending Jalview with Groovy - A customisable
+      feature counter</strong><br /> <br />The groovy script below shows how to
+    add a new calculation track to a Jalview alignment window.
+  </p>
+  <p>As currently written, it will add two tracks to a protein
+    alignment view which count Pfam features in each column, and ones
+    where a charge residue also occur.</p>
+  <p>To try it for yourself:</p>
+  <ol>
+    <li>Copy and paste it into the groovy script console</li>
+    <li>Load the example Feredoxin project (the one that opens by
+      default when you first launched Jalview)</li>
+    <li>Select <strong>Calculations&#8594;Execute Groovy
+        Script</strong> from the alignment window's menu bar to run the script on
+      the current view.
+    </li>
+  </ol>
+  <strong>Please note: The 2.10.2 feature counting interface is not compatible with earlier versions.</strong><br/><br/>
+  <em><a
+    href="http://www.jalview.org/examples/groovy/featuresCounter.groovy">http://www.jalview.org/examples/groovy/featuresCounter.groovy</a>
+    - rendered with <a href="http://hilite.me">hilite.me</a></em>
+  <!-- HTML generated using hilite.me --><div style="background: #f8f8f8; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #408080; font-style: italic">/*</span>
+<span style="color: #408080; font-style: italic"> * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)</span>
+<span style="color: #408080; font-style: italic"> * Copyright (C) $$Year-Rel$$ The Jalview Authors</span>
+<span style="color: #408080; font-style: italic"> * </span>
+<span style="color: #408080; font-style: italic"> * This file is part of Jalview.</span>
+<span style="color: #408080; font-style: italic"> * </span>
+<span style="color: #408080; font-style: italic"> * Jalview is free software: you can redistribute it and/or</span>
+<span style="color: #408080; font-style: italic"> * modify it under the terms of the GNU General Public License </span>
+<span style="color: #408080; font-style: italic"> * as published by the Free Software Foundation, either version 3</span>
+<span style="color: #408080; font-style: italic"> * of the License, or (at your option) any later version.</span>
+<span style="color: #408080; font-style: italic"> *  </span>
+<span style="color: #408080; font-style: italic"> * Jalview is distributed in the hope that it will be useful, but </span>
+<span style="color: #408080; font-style: italic"> * WITHOUT ANY WARRANTY; without even the implied warranty </span>
+<span style="color: #408080; font-style: italic"> * of MERCHANTABILITY or FITNESS FOR A PARTICULAR </span>
+<span style="color: #408080; font-style: italic"> * PURPOSE.  See the GNU General Public License for more details.</span>
+<span style="color: #408080; font-style: italic"> * </span>
+<span style="color: #408080; font-style: italic"> * You should have received a copy of the GNU General Public License</span>
+<span style="color: #408080; font-style: italic"> * along with Jalview.  If not, see &lt;http://www.gnu.org/licenses/&gt;.</span>
+<span style="color: #408080; font-style: italic"> * The Jalview Authors are detailed in the &#39;AUTHORS&#39; file.</span>
+<span style="color: #408080; font-style: italic"> */</span>
+
+<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">jalview.workers.AlignmentAnnotationFactory</span><span style="color: #666666">;</span>
+<span style="color: #008000; font-weight: bold">import</span> <span style="color: #0000FF; font-weight: bold">jalview.workers.FeatureSetCounterI</span><span style="color: #666666">;</span>
+
+<span style="color: #408080; font-style: italic">/*</span>
+<span style="color: #408080; font-style: italic"> * Example script to compute two alignment annotations</span>
+<span style="color: #408080; font-style: italic"> * - count of Phosphorylation features</span>
+<span style="color: #408080; font-style: italic"> * - count of Turn features</span>
+<span style="color: #408080; font-style: italic"> * To try this, first load example file uniref50.fa and load on features file</span>
+<span style="color: #408080; font-style: italic"> * exampleFeatures.txt, before running this script</span>
+<span style="color: #408080; font-style: italic"> *</span>
+<span style="color: #408080; font-style: italic"> * The script only needs to be run once - it will be registered by Jalview</span>
+<span style="color: #408080; font-style: italic"> * and recalculated automatically when the alignment changes.</span>
+<span style="color: #408080; font-style: italic"> * </span>
+<span style="color: #408080; font-style: italic"> * Note: The feature api provided by 2.10.2 is not compatible with scripts</span>
+<span style="color: #408080; font-style: italic"> * that worked with earlier Jalview versions. Apologies for the inconvenience.</span>
+<span style="color: #408080; font-style: italic"> */</span>
+<span style="color: #B00040">def</span> annotator <span style="color: #666666">=</span> 
+    <span style="color: #666666">[</span>
+     <span style="color: #A0A000">getNames:</span> <span style="color: #666666">{</span> <span style="color: #666666">[</span><span style="color: #BA2121">&#39;Phosphorylation&#39;</span><span style="color: #666666">,</span> <span style="color: #BA2121">&#39;Turn&#39;</span><span style="color: #666666">]</span> <span style="color: #008000; font-weight: bold">as</span> String<span style="color: #666666">[]</span> <span style="color: #666666">},</span> 
+     <span style="color: #A0A000">getDescriptions:</span>  <span style="color: #666666">{</span> <span style="color: #666666">[</span><span style="color: #BA2121">&#39;Count of Phosphorylation features&#39;</span><span style="color: #666666">,</span> <span style="color: #BA2121">&#39;Count of Turn features&#39;</span><span style="color: #666666">]</span> <span style="color: #008000; font-weight: bold">as</span> String<span style="color: #666666">[]</span> <span style="color: #666666">},</span>
+     <span style="color: #A0A000">getMinColour:</span> <span style="color: #666666">{</span> <span style="color: #666666">[0,</span> <span style="color: #666666">255,</span> <span style="color: #666666">255]</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #B00040">int</span><span style="color: #666666">[]</span> <span style="color: #666666">},</span> <span style="color: #408080; font-style: italic">// cyan</span>
+     <span style="color: #A0A000">getMaxColour:</span> <span style="color: #666666">{</span> <span style="color: #666666">[0,</span> <span style="color: #666666">0,</span> <span style="color: #666666">255]</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #B00040">int</span><span style="color: #666666">[]</span> <span style="color: #666666">},</span> <span style="color: #408080; font-style: italic">// blue</span>
+     <span style="color: #A0A000">count:</span> 
+         <span style="color: #666666">{</span> res<span style="color: #666666">,</span> feats <span style="color: #666666">-&gt;</span> 
+                <span style="color: #B00040">int</span> phos
+                <span style="color: #B00040">int</span> turn
+                <span style="color: #0000FF">for</span> <span style="color: #666666">(</span>sf <span style="color: #008000; font-weight: bold">in</span> feats<span style="color: #666666">)</span>
+                <span style="color: #666666">{</span>
+                         <span style="color: #408080; font-style: italic">/*</span>
+<span style="color: #408080; font-style: italic">                         * Here we inspect the type of the sequence feature.</span>
+<span style="color: #408080; font-style: italic">                         * You can also test sf.description, sf.score, sf.featureGroup,</span>
+<span style="color: #408080; font-style: italic">                         * sf.strand, sf.phase, sf.begin, sf.end</span>
+<span style="color: #408080; font-style: italic">                         * or sf.getValue(attributeName) for GFF &#39;column 9&#39; properties</span>
+<span style="color: #408080; font-style: italic">                         */</span>
+                          <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">(</span>sf<span style="color: #666666">.</span><span style="color: #7D9029">type</span><span style="color: #666666">.</span><span style="color: #7D9029">contains</span><span style="color: #666666">(</span><span style="color: #BA2121">&#39;TURN&#39;</span><span style="color: #666666">))</span>
+                   <span style="color: #666666">{</span>
+                      turn<span style="color: #666666">++</span>
+                   <span style="color: #666666">}</span>
+                   <span style="color: #008000; font-weight: bold">if</span> <span style="color: #666666">(</span>sf<span style="color: #666666">.</span><span style="color: #7D9029">type</span><span style="color: #666666">.</span><span style="color: #7D9029">contains</span><span style="color: #666666">(</span><span style="color: #BA2121">&#39;PHOSPHORYLATION&#39;</span><span style="color: #666666">))</span>
+                   <span style="color: #666666">{</span>
+                      phos<span style="color: #666666">++</span>
+                   <span style="color: #666666">}</span>
+                <span style="color: #666666">}</span>
+                <span style="color: #666666">[</span>phos<span style="color: #666666">,</span> turn<span style="color: #666666">]</span> <span style="color: #008000; font-weight: bold">as</span> <span style="color: #B00040">int</span><span style="color: #666666">[]</span>
+         <span style="color: #666666">}</span>
+     <span style="color: #666666">]</span> <span style="color: #008000; font-weight: bold">as</span> FeatureSetCounterI
+    
+<span style="color: #408080; font-style: italic">/*</span>
+<span style="color: #408080; font-style: italic"> * Register the annotation calculator with Jalview</span>
+<span style="color: #408080; font-style: italic"> */</span>
+AlignmentAnnotationFactory<span style="color: #666666">.</span><span style="color: #7D9029">newCalculator</span><span style="color: #666666">(</span>annotator<span style="color: #666666">)</span> 
+</pre></div>
+</body>
+</html>
index b571a5b..0a93e7a 100644 (file)
@@ -381,10 +381,8 @@ label.remove_from_default_list = Remove from default list?
 label.remove_user_defined_colour = Remove user defined colour
 label.you_must_select_least_two_sequences = You must select at least 2 sequences.
 label.invalid_selection = Invalid Selection
-label.principal_component_analysis_must_take_least_four_input_sequences = Principal component analysis must take\nat least 4 input sequences.
 label.sequence_selection_insufficient = Sequence selection insufficient
-label.you_need_more_two_sequences_selected_build_tree = You need to have more than two sequences selected to build a tree!
-label.you_need_more_than_n_sequences = You need to have more than {0} sequences
+label.you_need_at_least_n_sequences = You need to select at least {0} sequences
 label.not_enough_sequences = Not enough sequences
 label.selected_region_to_tree_may_only_contain_residues_or_gaps =  The selected region to create a tree may\nonly contain residues or gaps.\nTry using the Pad function in the edit menu,\nor one of the multiple sequence alignment web services.
 label.sequences_selection_not_aligned = Sequences in selection are not aligned
index 5932698..563684b 100644 (file)
@@ -177,7 +177,7 @@ label.score_model_pid = % Identidad
 label.score_model_blosum62 = BLOSUM62
 label.score_model_pam250 = PAM 250
 label.score_model_smithwatermanscore = Puntuación entre secuencias alineadas por Smith-Waterman con matriz por defecto proteica / nucleotídica
-label.score_model_sequencefeaturesimilarity = Medida de distancia por cuenta promedia de características no compartidas at sequence positions 
+label.score_model_sequencefeaturesimilarity = Medida de distancia por cuenta promedia de características no compartidas en posiciones de secuencia
 label.score_model_conservation = Conservación de las propiedades físico-químicas
 label.score_model_enhconservation = Conservación de las propiedades físico-químicas
 label.status_bar = Barra de estado
@@ -349,9 +349,8 @@ label.remove_from_default_list = eliminar de la lista de defectuosos?
 label.remove_user_defined_colour = Eliminar el color definido por el usuario
 label.you_must_select_least_two_sequences = Debes seleccionar al menos 2 secuencias.
 label.invalid_selection = Selección inválida
-label.principal_component_analysis_must_take_least_four_input_sequences = El an\u00E1lisis de la componente principal debe tomar\nal menos 4 secuencias de entrada.
 label.sequence_selection_insufficient = Selección de secuencias insuficiente
-label.you_need_more_two_sequences_selected_build_tree = necesitas seleccionar más de dos secuencias para construir un Ã¡rbol!
+label.you_need_at_least_n_sequences = Necesitas seleccionar al menos {0} secuencias
 label.not_enough_sequences = No suficientes secuencias
 label.selected_region_to_tree_may_only_contain_residues_or_gaps = La regi\u00F3n seleccionada para construir un \u00E1rbol puede\ncontener s\u00F3lo residuos o espacios.\nPrueba usando la funci\u00F3n Pad en el men\u00FA de edici\u00F3n,\n o uno de los m\u00FAltiples servicios web de alineamiento de secuencias.
 label.sequences_selection_not_aligned = Las secuencias seleccionadas no están alineadas
@@ -1301,4 +1300,10 @@ warn.name_cannot_be_duplicate = Los nombres URL definidos por el usuario deben s
 label.invalid_name = Nombre inválido !
 label.output_seq_details = Seleccionar Detalles de la secuencia para ver todas
 label.urllinks = Enlaces
-label.togglehidden = Show hidden regions
\ No newline at end of file
+label.quality_descr = Calidad de alineamiento basándose en puntuación Blosum62
+label.conservation_descr = Conservación del alineamiento total menos de {0}% huecos
+label.consensus_descr = % Identidad
+label.complement_consensus_descr = % Identidad para cDNA
+label.strucconsensus_descr = % Identidad para pares de bases
+label.occupancy_descr = Número de posiciones alineadas
+label.togglehidden = Show hidden regions
index e47c50a..4cc4a3a 100755 (executable)
@@ -35,6 +35,7 @@ import java.awt.event.InputEvent;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
@@ -225,6 +226,10 @@ public class IdPanel extends Panel implements MouseListener,
     String id = sq.getName();
 
     // get the default url with the sequence details filled in
+    if (urlProvider == null)
+    {
+      return;
+    }
     String url = urlProvider.getPrimaryUrl(id);
     String target = urlProvider.getPrimaryTarget(id);
     try
@@ -287,8 +292,15 @@ public class IdPanel extends Panel implements MouseListener,
 
       // build a new links menu based on the current links + any non-positional
       // features
-      List<String> nlinks = urlProvider.getLinksForMenu();
-
+      List<String> nlinks;
+      if (urlProvider != null)
+      {
+        nlinks = urlProvider.getLinksForMenu();
+      }
+      else
+      {
+        nlinks = new ArrayList<String>();
+      }
       SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
       for (int sl = 0; sf != null && sl < sf.length; sl++)
       {
index 78ed4a3..38031e4 100755 (executable)
@@ -115,7 +115,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
 
   void getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
   {
-    if (shader != null)
+    if (shader.getColourScheme() != null)
     {
       resBoxColour = shader.findColour(seq.getCharAt(i), i, seq);
     }
index 71ebbb3..8de8eb2 100755 (executable)
@@ -30,6 +30,12 @@ import java.awt.Color;
  */
 public class Annotation
 {
+  /**
+   * the empty annotation - proxy for null entries in annotation row
+   */
+  public static final Annotation EMPTY_ANNOTATION = new Annotation("", "",
+          ' ', 0f);
+
   /** Character label - also shown below histogram */
   public String displayCharacter = "";
 
@@ -192,4 +198,18 @@ public class Annotation
     }
     return sb.toString();
   }
+
+  /**
+   * @return true if annot is 'whitespace' annotation (zero score, whitespace or
+   *         zero length display character, label, description
+   */
+  public boolean isWhitespace()
+  {
+    return ((value == 0f)
+            && ((description == null) || (description.trim()
+                    .length() == 0))
+            && ((displayCharacter == null) || (displayCharacter
+                    .trim().length() == 0))
+            && (secondaryStructure == '\0' || (secondaryStructure == ' ')) && colour == null);
+  }
 }
index 2becfea..4073e3e 100644 (file)
@@ -3591,19 +3591,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     if (viewport.getSelectionGroup() != null
             && viewport.getSelectionGroup().getSize() > 0)
     {
-      if (viewport.getSelectionGroup().getSize() < 3)
-      {
-        JvOptionPane
-                .showMessageDialog(
-                        Desktop.desktop,
-                        MessageManager
-                                .getString("label.you_need_more_two_sequences_selected_build_tree"),
-                        MessageManager
-                                .getString("label.not_enough_sequences"),
-                        JvOptionPane.WARNING_MESSAGE);
-        return;
-      }
-
       SequenceGroup sg = viewport.getSelectionGroup();
 
       /* Decide if the selection is a column region */
index 05f1fba..8a95594 100644 (file)
@@ -25,6 +25,7 @@ import jalview.analysis.scoremodels.ScoreModels;
 import jalview.analysis.scoremodels.SimilarityParams;
 import jalview.api.analysis.ScoreModelI;
 import jalview.api.analysis.SimilarityParamsI;
+import jalview.datamodel.SequenceGroup;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -74,6 +75,10 @@ public class CalculationChooser extends JPanel
 
   private static final Font VERDANA_11PT = new Font("Verdana", 0, 11);
 
+  private static final int MIN_TREE_SELECTION = 3;
+
+  private static final int MIN_PCA_SELECTION = 4;
+
   AlignFrame af;
 
   JRadioButton pca;
@@ -84,7 +89,7 @@ public class CalculationChooser extends JPanel
 
   JComboBox<String> modelNames;
 
-  JButton ok;
+  JButton calculate;
 
   private JInternalFrame frame;
 
@@ -96,6 +101,10 @@ public class CalculationChooser extends JPanel
 
   private JCheckBox shorterSequence;
 
+  final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
+
+  List<String> tips = new ArrayList<String>();
+
   /**
    * Constructor
    * 
@@ -143,6 +152,7 @@ public class CalculationChooser extends JPanel
     pca.setOpaque(false);
     neighbourJoining = new JRadioButton(
             MessageManager.getString("label.tree_calc_nj"));
+    neighbourJoining.setSelected(true);
     averageDistance = new JRadioButton(
             MessageManager.getString("label.tree_calc_av"));
     neighbourJoining.setOpaque(false);
@@ -167,7 +177,6 @@ public class CalculationChooser extends JPanel
     pcaBorderless.add(pca, FlowLayout.LEFT);
     calcChoicePanel.add(pcaBorderless, FlowLayout.LEFT);
 
-
     treePanel.add(neighbourJoining);
     treePanel.add(averageDistance);
 
@@ -189,6 +198,7 @@ public class CalculationChooser extends JPanel
     pca.addActionListener(calcChanged);
     neighbourJoining.addActionListener(calcChanged);
     averageDistance.addActionListener(calcChanged);
+
     /*
      * score models drop-down - with added tooltips!
      */
@@ -216,30 +226,30 @@ public class CalculationChooser extends JPanel
     /*
      * OK / Cancel buttons
      */
-    ok = new JButton(MessageManager.getString("action.calculate"));
-    ok.setFont(VERDANA_11PT);
-    ok.addActionListener(new java.awt.event.ActionListener()
+    calculate = new JButton(MessageManager.getString("action.calculate"));
+    calculate.setFont(VERDANA_11PT);
+    calculate.addActionListener(new java.awt.event.ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        ok_actionPerformed();
+        calculate_actionPerformed();
       }
     });
-    JButton cancel = new JButton(MessageManager.getString("action.close"));
-    cancel.setFont(VERDANA_11PT);
-    cancel.addActionListener(new java.awt.event.ActionListener()
+    JButton close = new JButton(MessageManager.getString("action.close"));
+    close.setFont(VERDANA_11PT);
+    close.addActionListener(new java.awt.event.ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        cancel_actionPerformed(e);
+        close_actionPerformed();
       }
     });
     JPanel actionPanel = new JPanel();
     actionPanel.setOpaque(false);
-    actionPanel.add(ok);
-    actionPanel.add(cancel);
+    actionPanel.add(calculate);
+    actionPanel.add(close);
 
     boolean includeParams = false;
     this.add(calcChoicePanel, BorderLayout.CENTER);
@@ -260,9 +270,7 @@ public class CalculationChooser extends JPanel
       title = title + " (" + af.getViewport().viewName + ")";
     }
 
-    Desktop.addInternalFrame(frame,
-            title, width,
-            height, false);
+    Desktop.addInternalFrame(frame, title, width, height, false);
     calcChoicePanel.doLayout();
     revalidate();
     /*
@@ -290,18 +298,27 @@ public class CalculationChooser extends JPanel
     {
       size = af.getViewport().getSelectionGroup().getSize();
     }
-    if (!(checkEnabled(pca, size, 4)
-            | checkEnabled(neighbourJoining, size, 3) | checkEnabled(
-              averageDistance, size, 3)))
+
+    /*
+     * disable calc options for which there is insufficient input data
+     * return value of true means enabled and selected
+     */
+    boolean checkPca = checkEnabled(pca, size, MIN_PCA_SELECTION);
+    boolean checkNeighbourJoining = checkEnabled(neighbourJoining, size,
+            MIN_TREE_SELECTION);
+    boolean checkAverageDistance = checkEnabled(averageDistance, size,
+            MIN_TREE_SELECTION);
+
+    if (checkPca || checkNeighbourJoining || checkAverageDistance)
     {
-      ok.setToolTipText(null);
-      ok.setEnabled(true);
+      calculate.setToolTipText(null);
+      calculate.setEnabled(true);
     }
     else
     {
-      ok.setEnabled(false);
+      calculate.setEnabled(false);
     }
-    updateScoreModels(comboBox, tips);
+    updateScoreModels(modelNames, tips);
   }
 
   /**
@@ -314,12 +331,12 @@ public class CalculationChooser extends JPanel
    *          - size of input to calculation
    * @param minsize
    *          - minimum size for calculation
-   * @return true if size < minsize *and* calc.isSelected
+   * @return true if size >= minsize and calc.isSelected
    */
   private boolean checkEnabled(JRadioButton calc, int size, int minsize)
   {
     String ttip = MessageManager.formatMessage(
-            "label.you_need_more_than_n_sequences", minsize);
+            "label.you_need_at_least_n_sequences", minsize);
 
     calc.setEnabled(size >= minsize);
     if (!calc.isEnabled())
@@ -333,22 +350,18 @@ public class CalculationChooser extends JPanel
     if (calc.isSelected())
     {
       modelNames.setEnabled(calc.isEnabled());
-      if (!calc.isEnabled())
+      if (calc.isEnabled())
       {
-        ok.setEnabled(false);
-        ok.setToolTipText(ttip);
         return true;
       }
+      else
+      {
+        calculate.setToolTipText(ttip);
+      }
     }
     return false;
   }
 
-  final JComboBox<String> comboBox = new JComboBox<String>();
-
-  final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
-
-  List<String> tips = new ArrayList<String>();
-
   /**
    * A rather elaborate helper method (blame Swing, not me) that builds a
    * drop-down list of score models (by name) with descriptions as tooltips.
@@ -357,7 +370,8 @@ public class CalculationChooser extends JPanel
    */
   protected JComboBox<String> buildModelOptionsList()
   {
-    comboBox.setRenderer(renderer);
+    final JComboBox<String> scoreModelsCombo = new JComboBox<String>();
+    scoreModelsCombo.setRenderer(renderer);
 
     /*
      * show tooltip on mouse over the combobox
@@ -369,35 +383,36 @@ public class CalculationChooser extends JPanel
       @Override
       public void mouseEntered(MouseEvent e)
       {
-        comboBox.setToolTipText(tips.get(comboBox.getSelectedIndex()));
+        scoreModelsCombo.setToolTipText(tips.get(scoreModelsCombo.getSelectedIndex()));
       }
 
       @Override
       public void mouseExited(MouseEvent e)
       {
-        comboBox.setToolTipText(null);
+        scoreModelsCombo.setToolTipText(null);
       }
     };
-    for (Component c : comboBox.getComponents())
+    for (Component c : scoreModelsCombo.getComponents())
     {
       c.addMouseListener(mouseListener);
     }
 
-    updateScoreModels(comboBox, tips);
+    updateScoreModels(scoreModelsCombo, tips);
 
     /*
      * set the list of tooltips on the combobox's renderer
      */
     renderer.setTooltips(tips);
 
-    return comboBox;
+    return scoreModelsCombo;
   }
 
-  private void updateScoreModels(JComboBox comboBox, List<String> tips)
+  private void updateScoreModels(JComboBox<String> comboBox,
+          List<String> toolTips)
   {
     Object curSel = comboBox.getSelectedItem();
-    tips.clear();
-    DefaultComboBoxModel model = new DefaultComboBoxModel();
+    toolTips.clear();
+    DefaultComboBoxModel<String> model = new DefaultComboBoxModel<String>();
 
     /*
      * now we can actually add entries to the combobox,
@@ -427,7 +442,7 @@ public class CalculationChooser extends JPanel
           tooltip = MessageManager.getStringOrReturn("label.score_model_",
                   sm.getName());
         }
-        tips.add(tooltip);
+        toolTips.add(tooltip);
       }
     }
     if (selectedIsPresent)
@@ -441,7 +456,7 @@ public class CalculationChooser extends JPanel
   /**
    * Open and calculate the selected tree or PCA on 'OK'
    */
-  protected void ok_actionPerformed()
+  protected void calculate_actionPerformed()
   {
     boolean doPCA = pca.isSelected();
     String modelName = modelNames.getSelectedItem().toString();
@@ -467,6 +482,26 @@ public class CalculationChooser extends JPanel
    */
   protected void openTreePanel(String modelName, SimilarityParamsI params)
   {
+    /*
+     * gui validation shouldn't allow insufficient sequences here, but leave
+     * this check in in case this method gets exposed programmatically in future
+     */
+    AlignViewport viewport = af.getViewport();
+    SequenceGroup sg = viewport.getSelectionGroup();
+    if (sg != null && sg.getSize() < MIN_TREE_SELECTION)
+    {
+      JvOptionPane
+              .showMessageDialog(
+                      Desktop.desktop,
+                      MessageManager
+              .formatMessage("label.you_need_at_least_n_sequences",
+                      MIN_TREE_SELECTION),
+                      MessageManager
+                              .getString("label.not_enough_sequences"),
+                      JvOptionPane.WARNING_MESSAGE);
+      return;
+    }
+
     String treeType = neighbourJoining.isSelected() ? TreeBuilder.NEIGHBOUR_JOINING
             : TreeBuilder.AVERAGE_DISTANCE;
     af.newTreePanel(treeType, modelName, params);
@@ -481,19 +516,21 @@ public class CalculationChooser extends JPanel
   protected void openPcaPanel(String modelName, SimilarityParamsI params)
   {
     AlignViewport viewport = af.getViewport();
+
+    /*
+     * gui validation shouldn't allow insufficient sequences here, but leave
+     * this check in in case this method gets exposed programmatically in future
+     */
     if (((viewport.getSelectionGroup() != null)
-            && (viewport.getSelectionGroup().getSize() < 4) && (viewport
+            && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION) && (viewport
             .getSelectionGroup().getSize() > 0))
-            || (viewport.getAlignment().getHeight() < 4))
+            || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION))
     {
-      JvOptionPane
-              .showInternalMessageDialog(
-                      this,
-                      MessageManager
-                              .getString("label.principal_component_analysis_must_take_least_four_input_sequences"),
-                      MessageManager
-                              .getString("label.sequence_selection_insufficient"),
-                      JvOptionPane.WARNING_MESSAGE);
+      JvOptionPane.showInternalMessageDialog(this, MessageManager
+              .formatMessage("label.you_need_at_least_n_sequences",
+                      MIN_PCA_SELECTION), MessageManager
+              .getString("label.sequence_selection_insufficient"),
+              JvOptionPane.WARNING_MESSAGE);
       return;
     }
     new PCAPanel(af.alignPanel, modelName, params);
@@ -542,11 +579,9 @@ public class CalculationChooser extends JPanel
   }
 
   /**
-   * Closes dialog on cancel
-   * 
-   * @param e
+   * Closes dialog on Close button press
    */
-  protected void cancel_actionPerformed(ActionEvent e)
+  protected void close_actionPerformed()
   {
     try
     {
index c2f4c75..ab2e66b 100644 (file)
@@ -397,7 +397,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       {
         ap.scrollUp(true);
       }
-      while (seqCanvas.cursorY + 1 > av.getRanges().getEndSeq())
+      while (seqCanvas.cursorY > av.getRanges().getEndSeq())
       {
         ap.scrollUp(false);
       }
index 2a2a0cf..36825ea 100755 (executable)
@@ -138,7 +138,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
    */
   void getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
   {
-    if (shader != null)
+    if (shader.getColourScheme() != null)
     {
       resBoxColour = shader.findColour(seq.getCharAt(i),
               i, seq);
index a47e1ea..e4d9f60 100644 (file)
@@ -45,7 +45,6 @@ import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.StringTokenizer;
 import java.util.Vector;
 
 import com.stevesoft.pat.Regex;
@@ -78,8 +77,8 @@ public class StockholmFile extends AlignFile
 
   private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")");
 
-  private static final Regex DETECT_BRACKETS = new Regex(
-          "(<|>|\\[|\\]|\\(|\\))");
+  public static final Regex DETECT_BRACKETS = new Regex(
+          "(<|>|\\[|\\]|\\(|\\)|\\{|\\})");
 
   StringBuffer out; // output buffer
 
@@ -366,6 +365,11 @@ public class StockholmFile extends AlignFile
 
               // add alignment annotation for this feature
               String key = type2id(type);
+
+              /*
+               * have we added annotation rows for this type ?
+               */
+              boolean annotsAdded = false;
               if (key != null)
               {
                 if (accAnnotations != null
@@ -374,6 +378,7 @@ public class StockholmFile extends AlignFile
                   Vector vv = (Vector) accAnnotations.get(key);
                   for (int ii = 0; ii < vv.size(); ii++)
                   {
+                    annotsAdded = true;
                     AlignmentAnnotation an = (AlignmentAnnotation) vv
                             .elementAt(ii);
                     seqO.addAlignmentAnnotation(an);
@@ -386,6 +391,11 @@ public class StockholmFile extends AlignFile
               while (j.hasMoreElements())
               {
                 String desc = j.nextElement().toString();
+                if ("annotations".equals(desc) && annotsAdded)
+                {
+                  // don't add features if we already added an annotation row
+                  continue;
+                }
                 String ns = content.get(desc).toString();
                 char[] byChar = ns.toCharArray();
                 for (int k = 0; k < byChar.length; k++)
@@ -572,22 +582,11 @@ public class StockholmFile extends AlignFile
           {
             String acc = s.stringMatched(1);
             String type = s.stringMatched(2);
-            String seq = new String(s.stringMatched(3));
-            String description = null;
-            // Check for additional information about the current annotation
-            // We use a simple string tokenizer here for speed
-            StringTokenizer sep = new StringTokenizer(seq, " \t");
-            description = sep.nextToken();
-            if (sep.hasMoreTokens())
-            {
-              seq = sep.nextToken();
-            }
-            else
-            {
-              seq = description;
-              description = new String();
-            }
-            // sequence id with from-to fields
+            String oseq = s.stringMatched(3);
+            /*
+             * copy of annotation field that may be processed into whitespace chunks
+             */
+            String seq = new String(oseq);
 
             Hashtable ann;
             // Get an object with all the annotations for this sequence
@@ -602,8 +601,12 @@ public class StockholmFile extends AlignFile
               ann = new Hashtable();
               seqAnn.put(acc, ann);
             }
+
+            // // start of block for appending annotation lines for wrapped
+            // stokchholm file
             // TODO test structure, call parseAnnotationRow with vector from
             // hashtable for specific sequence
+
             Hashtable features;
             // Get an object with all the content for an annotation
             if (ann.containsKey("features"))
@@ -631,15 +634,18 @@ public class StockholmFile extends AlignFile
               content = new Hashtable();
               features.put(this.id2type(type), content);
             }
-            String ns = (String) content.get(description);
+            String ns = (String) content.get("annotation");
+
             if (ns == null)
             {
               ns = "";
             }
+            // finally, append the annotation line
             ns += seq;
-            content.put(description, ns);
+            content.put("annotation", ns);
+            // // end of wrapped annotation block.
+            // // Now a new row is created with the current set of data
 
-            // if(type.equals("SS")){
             Hashtable strucAnn;
             if (seqAnn.containsKey(acc))
             {
@@ -656,7 +662,8 @@ public class StockholmFile extends AlignFile
             {
               alan.visible = false;
             }
-            // annotations.addAll(newStruc);
+            // new annotation overwrites any existing annotation...
+
             strucAnn.put(type, newStruc);
             seqAnn.put(acc, strucAnn);
           }
@@ -831,12 +838,12 @@ public class StockholmFile extends AlignFile
           if (DETECT_BRACKETS.search(pos))
           {
             ann.secondaryStructure = Rna.getRNASecStrucState(pos).charAt(0);
+            ann.displayCharacter = "" + pos.charAt(0);
           }
           else
           {
             ann.secondaryStructure = ResidueProperties.getDssp3state(pos)
                     .charAt(0);
-          }
 
           if (ann.secondaryStructure == pos.charAt(0))
           {
@@ -846,6 +853,7 @@ public class StockholmFile extends AlignFile
           {
             ann.displayCharacter = " " + ann.displayCharacter;
           }
+          }
         }
 
       }
@@ -966,46 +974,39 @@ public class StockholmFile extends AlignFile
     // output annotations
     while (i < s.length && s[i] != null)
     {
-      if (s[i].getDatasetSequence() != null)
+      AlignmentAnnotation[] alAnot = s[i].getAnnotation();
+      if (alAnot != null)
       {
-        SequenceI ds = s[i].getDatasetSequence();
-        AlignmentAnnotation[] alAnot;
         Annotation[] ann;
-        Annotation annot;
-        alAnot = s[i].getAnnotation();
-        String feature = "";
-        if (alAnot != null)
+        for (int j = 0; j < alAnot.length; j++)
         {
-          for (int j = 0; j < alAnot.length; j++)
-          {
 
-            String key = type2id(alAnot[j].label);
-            boolean isrna = alAnot[j].isValidStruc();
+          String key = type2id(alAnot[j].label);
+          boolean isrna = alAnot[j].isValidStruc();
 
-            if (isrna)
-            {
-              // hardwire to secondary structure if there is RNA secondary
-              // structure on the annotation
-              key = "SS";
-            }
-            if (key == null)
-            {
+          if (isrna)
+          {
+            // hardwire to secondary structure if there is RNA secondary
+            // structure on the annotation
+            key = "SS";
+          }
+          if (key == null)
+          {
 
-              continue;
-            }
+            continue;
+          }
 
-            // out.append("#=GR ");
-            out.append(new Format("%-" + maxid + "s").form("#=GR "
-                    + printId(s[i], jvSuffix) + " " + key + " "));
-            ann = alAnot[j].annotations;
-            String seq = "";
-            for (int k = 0; k < ann.length; k++)
-            {
-              seq += outputCharacter(key, k, isrna, ann, s[i]);
-            }
-            out.append(seq);
-            out.append(newline);
+          // out.append("#=GR ");
+          out.append(new Format("%-" + maxid + "s").form("#=GR "
+                  + printId(s[i], jvSuffix) + " " + key + " "));
+          ann = alAnot[j].annotations;
+          String seq = "";
+          for (int k = 0; k < ann.length; k++)
+          {
+            seq += outputCharacter(key, k, isrna, ann, s[i]);
           }
+          out.append(seq);
+          out.append(newline);
         }
       }
 
@@ -1091,8 +1092,8 @@ public class StockholmFile extends AlignFile
     {
       if (annot == null)
       {
-        // sensible gap character if one is available or make one up
-        return sequenceI == null ? '-' : sequenceI.getCharAt(k);
+        // sensible gap character
+        return ' ';
       }
       else
       {
index beee1eb..b0392d4 100644 (file)
@@ -48,35 +48,17 @@ public class AlignmentAnnotationFactory
    * @param counter
    *          provider of feature counts per alignment position
    */
-  public static void newCalculator(FeatureCounterI counter)
+  public static void newCalculator(FeatureSetCounterI counter)
   {
-    // TODO need an interface for AlignFrame by which to access
-    // its AlignViewportI and AlignmentViewPanel
     AlignmentViewPanel currentAlignFrame = Jalview.getCurrentAlignFrame().alignPanel;
-    if (currentAlignFrame != null)
-    {
-      newCalculator(currentAlignFrame.getAlignViewport(),
-              currentAlignFrame, counter);
-    }
-    else
+    if (currentAlignFrame == null)
     {
       System.err
               .println("Can't register calculator as no alignment window has focus");
+      return;
     }
-  }
-
-  /**
-   * Constructs and registers a new alignment annotation worker
-   * 
-   * @param viewport
-   * @param panel
-   * @param counter
-   *          provider of feature counts per alignment position
-   */
-  public static void newCalculator(AlignViewportI viewport,
-          AlignmentViewPanel panel, FeatureCounterI counter)
-  {
-    new ColumnCounterWorker(viewport, panel, counter);
+    new ColumnCounterSetWorker(currentAlignFrame.getAlignViewport(),
+            currentAlignFrame, counter);
   }
 
   /**
@@ -92,8 +74,8 @@ public class AlignmentAnnotationFactory
     AlignFrame currentAlignFrame = Jalview.getCurrentAlignFrame();
     if (currentAlignFrame != null)
     {
-      newCalculator(currentAlignFrame.getViewport(), currentAlignFrame
-              .getAlignPanels().get(0), calculator);
+      new AnnotationWorker(currentAlignFrame.getViewport(),
+              currentAlignFrame.getAlignPanels().get(0), calculator);
     }
     else
     {
@@ -36,16 +36,16 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
- * A class to compute an alignment annotation with column counts of any
- * properties of interest of positions in an alignment. <br>
+ * A class to compute alignment annotations with column counts for a set of
+ * properties of interest on positions in an alignment. <br>
  * This is designed to be extensible, by supplying to the constructor an object
- * that computes a count for each residue position, based on the residue value
- * and any sequence features at that position.
+ * that computes a vector of counts for each residue position, based on the
+ * residue and and sequence features at that position.
  * 
  */
-class ColumnCounterWorker extends AlignCalcWorker
+class ColumnCounterSetWorker extends AlignCalcWorker
 {
-  FeatureCounterI counter;
+  FeatureSetCounterI counter;
 
   /**
    * Constructor registers the annotation for the given alignment frame
@@ -53,8 +53,8 @@ class ColumnCounterWorker extends AlignCalcWorker
    * @param af
    * @param counter
    */
-  public ColumnCounterWorker(AlignViewportI viewport,
-          AlignmentViewPanel panel, FeatureCounterI counter)
+  public ColumnCounterSetWorker(AlignViewportI viewport,
+          AlignmentViewPanel panel, FeatureSetCounterI counter)
   {
     super(viewport, panel);
     ourAnnots = new ArrayList<AlignmentAnnotation>();
@@ -69,6 +69,7 @@ class ColumnCounterWorker extends AlignCalcWorker
   @Override
   public void run()
   {
+    boolean annotationAdded = false;
     try
     {
       calcMan.notifyStart(this);
@@ -93,7 +94,7 @@ class ColumnCounterWorker extends AlignCalcWorker
       {
         try
         {
-          computeAnnotations();
+          annotationAdded = computeAnnotations();
         } catch (IndexOutOfBoundsException x)
         {
           // probable race condition. just finish and return without any fuss.
@@ -111,7 +112,10 @@ class ColumnCounterWorker extends AlignCalcWorker
 
     if (ap != null)
     {
-      ap.adjustAnnotationHeight();
+      if (annotationAdded)
+      {
+        ap.adjustAnnotationHeight();
+      }
       ap.paintAlignment(true);
     }
 
@@ -120,8 +124,10 @@ class ColumnCounterWorker extends AlignCalcWorker
   /**
    * Scan each column of the alignment to calculate a count by feature type. Set
    * the count as the value of the alignment annotation for that feature type.
+   * 
+   * @return
    */
-  void computeAnnotations()
+  boolean computeAnnotations()
   {
     FeatureRenderer fr = new FeatureRenderer(alignViewport);
     // TODO use the commented out code once JAL-2075 is fixed
@@ -130,56 +136,90 @@ class ColumnCounterWorker extends AlignCalcWorker
     // AlignmentView alignmentView = alignViewport.getAlignmentView(false);
     // AlignmentI alignment = alignmentView.getVisibleAlignment(' ');
 
-    // int width = alignmentView.getWidth();
+    int rows = counter.getNames().length;
+
     int width = alignment.getWidth();
     int height = alignment.getHeight();
-    int[] counts = new int[width];
-    int max = 0;
+    int[][] counts = new int[width][rows];
+    int max[] = new int[rows];
+    for (int crow = 0; crow < rows; crow++)
+    {
+      max[crow] = 0;
+    }
+
+    int[] minC = counter.getMinColour();
+    int[] maxC = counter.getMaxColour();
+    Color minColour = new Color(minC[0], minC[1], minC[2]);
+    Color maxColour = new Color(maxC[0], maxC[1], maxC[2]);
 
     for (int col = 0; col < width; col++)
     {
-      int count = 0;
+      int[] count = counts[col];
+      for (int crow = 0; crow < rows; crow++)
+      {
+        count[crow] = 0;
+      }
       for (int row = 0; row < height; row++)
       {
-        count += countFeaturesAt(alignment, col, row, fr);
+        int[] colcount = countFeaturesAt(alignment, col, row, fr);
+        if (colcount != null)
+        {
+          for (int crow = 0; crow < rows; crow++)
+          {
+            count[crow] += colcount[crow];
+          }
+        }
       }
       counts[col] = count;
-      max = Math.max(count, max);
+      for (int crow = 0; crow < rows; crow++)
+      {
+        max[crow] = Math.max(count[crow], max[crow]);
+      }
     }
 
-    Annotation[] anns = new Annotation[width];
-    /*
-     * add non-zero counts as annotations
-     */
-    for (int i = 0; i < counts.length; i++)
+    boolean annotationAdded = false;
+
+    for (int anrow = 0; anrow < rows; anrow++)
     {
-      int count = counts[i];
-      if (count > 0)
+      Annotation[] anns = new Annotation[width];
+      /*
+       * add non-zero counts as annotations
+       */
+      for (int i = 0; i < counts.length; i++)
       {
-        Color color = ColorUtils.getGraduatedColour(count, 0, Color.cyan,
-                max, Color.blue);
-        String str = String.valueOf(count);
-        anns[i] = new Annotation(str, str, '0', count, color);
+        int count = counts[i][anrow];
+        if (count > 0)
+        {
+          Color color = ColorUtils.getGraduatedColour(count, 0, minColour,
+                  max[anrow], maxColour);
+          String str = String.valueOf(count);
+          anns[i] = new Annotation(str, str, '0', count, color);
+        }
       }
-    }
 
-    /*
-     * construct or update the annotation
-     */
-    AlignmentAnnotation ann = alignViewport.getAlignment()
-            .findOrCreateAnnotation(counter.getName(),
-                    counter.getDescription(), false, null, null);
-    ann.description = counter.getDescription();
-    ann.showAllColLabels = true;
-    ann.scaleColLabel = true;
-    ann.graph = AlignmentAnnotation.BAR_GRAPH;
-    ann.annotations = anns;
-    setGraphMinMax(ann, anns);
-    ann.validateRangeAndDisplay();
-    if (!ourAnnots.contains(ann))
-    {
-      ourAnnots.add(ann);
+      /*
+       * construct or update the annotation
+       */
+      String description = counter.getDescriptions()[anrow];
+      if (!alignment.findAnnotation(description).iterator().hasNext())
+      {
+        annotationAdded = true;
+      }
+      AlignmentAnnotation ann = alignment.findOrCreateAnnotation(
+              counter.getNames()[anrow], description, false, null, null);
+      ann.description = description;
+      ann.showAllColLabels = true;
+      ann.scaleColLabel = true;
+      ann.graph = AlignmentAnnotation.BAR_GRAPH;
+      ann.annotations = anns;
+      setGraphMinMax(ann, anns);
+      ann.validateRangeAndDisplay();
+      if (!ourAnnots.contains(ann))
+      {
+        ourAnnots.add(ann);
+      }
     }
+    return annotationAdded;
   }
 
   /**
@@ -191,22 +231,22 @@ class ColumnCounterWorker extends AlignCalcWorker
    * @param row
    * @param fr
    */
-  int countFeaturesAt(AlignmentI alignment, int col, int row,
+  int[] countFeaturesAt(AlignmentI alignment, int col, int row,
           FeatureRenderer fr)
   {
     SequenceI seq = alignment.getSequenceAt(row);
     if (seq == null)
     {
-      return 0;
+      return null;
     }
     if (col >= seq.getLength())
     {
-      return 0;// sequence doesn't extend this far
+      return null;// sequence doesn't extend this far
     }
     char res = seq.getCharAt(col);
     if (Comparison.isGap(res))
     {
-      return 0;
+      return null;
     }
     int pos = seq.findPosition(col);
 
@@ -216,7 +256,7 @@ class ColumnCounterWorker extends AlignCalcWorker
     // NB have to adjust pos if using AlignmentView.getVisibleAlignment
     // see JAL-2075
     List<SequenceFeature> features = fr.findFeaturesAtRes(seq, pos);
-    int count = this.counter.count(String.valueOf(res), features);
+    int[] count = this.counter.count(String.valueOf(res), features);
     return count;
   }
 
similarity index 76%
rename from src/jalview/workers/FeatureCounterI.java
rename to src/jalview/workers/FeatureSetCounterI.java
index 3a080ec..e14952f 100644 (file)
@@ -18,6 +18,7 @@
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
+
 package jalview.workers;
 
 import jalview.datamodel.SequenceFeature;
@@ -25,15 +26,16 @@ import jalview.datamodel.SequenceFeature;
 import java.util.List;
 
 /**
- * An interface for a type that returns counts of any value of interest at a
- * sequence position that can be determined from the sequence character and any
- * features present at that position
+ * An interface for a type that returns counts (per computed annotation type) of
+ * any value of interest at a sequence position that can be determined from the
+ * sequence character and any features present at that position
  * 
  */
-public interface FeatureCounterI
+public interface FeatureSetCounterI
 {
   /**
-   * Returns a count of some property of interest, for example
+   * Returns counts (per annotation type) of some properties of interest, for
+   * example
    * <ul>
    * <li>the number of variant features at the position</li>
    * <li>the number of Cath features of status 'True Positive'</li>
@@ -46,22 +48,22 @@ public interface FeatureCounterI
    * @param a
    *          list of any sequence features which include the position
    */
-  int count(String residue, List<SequenceFeature> features);
+  int[] count(String residue, List<SequenceFeature> features);
 
   /**
-   * Returns a name for the annotation that this is counting, for use as the
-   * displayed label
+   * Returns names for the annotations that this is counting, for use as the
+   * displayed labels
    * 
    * @return
    */
-  String getName();
+  String[] getNames();
 
   /**
-   * Returns a description for the annotation, for display as a tooltip
+   * Returns descriptions for the annotations, for display as tooltips
    * 
    * @return
    */
-  String getDescription();
+  String[] getDescriptions();
 
   /**
    * Returns the colour (as [red, green, blue] values in the range 0-255) to use
index 29a9a52..c80b830 100644 (file)
@@ -58,6 +58,24 @@ public class SequenceRendererTest
     assertEquals(Color.magenta, sr.getResidueBoxColour(seq, 5)); // G
     assertEquals(Color.orange, sr.getResidueBoxColour(seq, 12)); // F
   }
+
+  @Test(groups = { "Functional" })
+  public void testGetResidueBoxColour_none()
+  {
+    SequenceI seq = new Sequence("name", "MA--TVLGSPRAPAFF");
+    AlignmentI al = new Alignment(new SequenceI[] { seq });
+    final AlignViewport av = new AlignViewport(al);
+    SequenceRenderer sr = new SequenceRenderer(av);
+
+    assertEquals(Color.white, sr.getResidueBoxColour(seq, 0));
+    assertEquals(Color.white, sr.getResidueBoxColour(seq, 2));
+
+    // set for overview
+    sr.forOverview = true;
+    assertEquals(Color.lightGray, sr.getResidueBoxColour(seq, 0));
+    assertEquals(Color.white, sr.getResidueBoxColour(seq, 2));
+  }
+
   // TODO more tests for getResidueBoxColour covering groups, feature rendering,
   // gaps, overview...
 
index e7f1435..1a18fb9 100644 (file)
@@ -33,10 +33,13 @@ import jalview.datamodel.SequenceI;
 import jalview.gui.JvOptionPane;
 
 import java.io.File;
+import java.util.Arrays;
 import java.util.BitSet;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
+import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -177,10 +180,12 @@ public class StockholmFileTest
   public static void testAlignmentEquivalence(AlignmentI al,
           AlignmentI al_input, boolean ignoreFeatures)
   {
+    testAlignmentEquivalence(al, al_input, ignoreFeatures, false, false);
   }
 
   /**
-   * assert alignment equivalence
+   * assert alignment equivalence - uses special comparators for RNA structure
+   * annotation rows.
    * 
    * @param al
    *          'original'
@@ -193,10 +198,15 @@ public class StockholmFileTest
    * @param ignoreRowVisibility
    *          when true, do not fail if there are differences in the visibility
    *          of annotation rows
+   * @param allowNullAnnotation
+   *          when true, positions in alignment annotation that are null will be
+   *          considered equal to positions containing annotation where
+   *          Annotation.isWhitespace() returns true.
+   * 
    */
   public static void testAlignmentEquivalence(AlignmentI al,
           AlignmentI al_input, boolean ignoreFeatures,
-          boolean ignoreRowVisibility)
+          boolean ignoreRowVisibility, boolean allowNullAnnotation)
   {
     assertNotNull("Original alignment was null", al);
     assertNotNull("Generated alignment was null", al_input);
@@ -227,7 +237,7 @@ public class StockholmFileTest
         {
           assertEqualSecondaryStructure(
                   "Different alignment annotation at position " + i,
-                  aa_original[i], aa_new[i]);
+                  aa_original[i], aa_new[i], allowNullAnnotation);
           // compare graphGroup or graph properties - needed to verify JAL-1299
           assertEquals("Graph type not identical.", aa_original[i].graph,
                   aa_new[i].graph);
@@ -341,7 +351,7 @@ public class StockholmFileTest
                 annot_new = al_input.getSequenceAt(in).getAnnotation()[j];
                 assertEqualSecondaryStructure(
                         "Different annotation elements", annot_original,
-                        annot_new);
+                        annot_new, allowNullAnnotation);
               }
             }
           }
@@ -363,9 +373,26 @@ public class StockholmFileTest
     }
   }
 
+  /**
+   * compare two annotation rows, with special support for secondary structure
+   * comparison. With RNA, only the value and the secondaryStructure symbols are
+   * compared, displayCharacter and description are ignored. Annotations where
+   * Annotation.isWhitespace() is true are always considered equal.
+   * 
+   * @param message
+   *          - not actually used yet..
+   * @param annot_or
+   *          - the original annotation
+   * @param annot_new
+   *          - the one compared to the original annotation
+   * @param allowNullEquivalence
+   *          when true, positions in alignment annotation that are null will be
+   *          considered equal to non-null positions for which
+   *          Annotation.isWhitespace() is true.
+   */
   private static void assertEqualSecondaryStructure(String message,
-          AlignmentAnnotation annot_or,
-          AlignmentAnnotation annot_new)
+          AlignmentAnnotation annot_or, AlignmentAnnotation annot_new,
+          boolean allowNullEqivalence)
   {
     // TODO: test to cover this assert behaves correctly for all allowed
     // variations of secondary structure annotation row equivalence
@@ -388,7 +415,8 @@ public class StockholmFileTest
         if (isRna)
         {
           if (an_or.secondaryStructure != an_new.secondaryStructure
-                  || an_or.value != an_new.value)
+                  || ((Float.isNaN(an_or.value) != Float
+                          .isNaN(an_new.value)) || an_or.value != an_new.value))
           {
             fail("Different RNA secondary structure at column " + i
                     + " expected: [" + annot_or.annotations[i].toString()
@@ -399,7 +427,8 @@ public class StockholmFileTest
         else
         {
           // not RNA secondary structure, so expect all elements to match...
-          if (!an_or.displayCharacter.trim().equals(
+          if ((an_or.isWhitespace() != an_new.isWhitespace())
+                  || !an_or.displayCharacter.trim().equals(
                   an_new.displayCharacter.trim())
                   || !("" + an_or.secondaryStructure).trim().equals(
                           ("" + an_new.secondaryStructure).trim())
@@ -407,7 +436,9 @@ public class StockholmFileTest
                           .trim().length() == 0)
                           || (an_new.description == null && an_or.description
                                   .trim().length() == 0) || an_or.description
-                          .trim().equals(an_new.description.trim()))))
+                          .trim().equals(an_new.description.trim())))
+                  || !((Float.isNaN(an_or.value) && Float
+                          .isNaN(an_new.value)) || an_or.value == an_new.value))
           {
             fail("Annotation Element Mismatch\nElement " + i
                     + " in original: " + annot_or.annotations[i].toString()
@@ -423,17 +454,202 @@ public class StockholmFileTest
       }
       else
       {
-        fail("Annotation Element Mismatch\nElement "
-                + i
-                + " in original: "
-                + (annot_or.annotations[i] == null ? "is null"
-                        : annot_or.annotations[i].toString())
-                + "\nElement "
-                + i
-                + " in new: "
-                + (annot_new.annotations[i] == null ? "is null"
-                        : annot_new.annotations[i].toString()));
+        if (allowNullEqivalence)
+        {
+          if (an_or != null && an_or.isWhitespace())
+
+          {
+            continue;
+          }
+          if (an_new != null && an_new.isWhitespace())
+          {
+            continue;
+          }
+        }
+        // need also to test for null in one, non-SS annotation in other...
+        fail("Annotation Element Mismatch\nElement " + i + " in original: "
+                + (an_or == null ? "is null" : an_or.toString())
+                + "\nElement " + i + " in new: "
+                + (an_new == null ? "is null" : an_new.toString()));
+      }
+    }
+  }
+
+  /**
+   * @see assertEqualSecondaryStructure - test if two secondary structure
+   *      annotations are not equal
+   * @param message
+   * @param an_orig
+   * @param an_new
+   * @param allowNullEquivalence
+   */
+  public static void assertNotEqualSecondaryStructure(String message,
+          AlignmentAnnotation an_orig, AlignmentAnnotation an_new,
+          boolean allowNullEquivalence)
+  {
+    boolean thrown = false;
+    try
+    {
+      assertEqualSecondaryStructure("", an_orig, an_new,
+              allowNullEquivalence);
+    } catch (AssertionError af)
+    {
+      thrown = true;
+    }
+    if (!thrown)
+    {
+      fail("Expected difference for [" + an_orig + "] and [" + an_new + "]");
+    }
+  }
+  private AlignmentAnnotation makeAnnot(Annotation ae)
+  {
+    return new AlignmentAnnotation("label", "description", new Annotation[]
+    { ae });
+  }
+
+  @Test(groups={"Functional"})
+  public void testAnnotationEquivalence()
+  {
+    AlignmentAnnotation one = makeAnnot(new Annotation("", "", ' ', 1));
+    AlignmentAnnotation anotherOne = makeAnnot(new Annotation("", "", ' ',
+            1));
+    AlignmentAnnotation sheet = makeAnnot(new Annotation("","",'E',0f));
+    AlignmentAnnotation anotherSheet = makeAnnot(new Annotation("","",'E',0f)); 
+    AlignmentAnnotation sheetWithLabel = makeAnnot(new Annotation("1", "",
+            'E', 0f));
+    AlignmentAnnotation anotherSheetWithLabel = makeAnnot(new Annotation(
+            "1", "", 'E', 0f));
+    AlignmentAnnotation rnaNoDC = makeAnnot(new Annotation("","",'<',0f));
+    AlignmentAnnotation anotherRnaNoDC = makeAnnot(new Annotation("","",'<',0f));
+    AlignmentAnnotation rnaWithDC = makeAnnot(new Annotation("B", "", '<',
+            0f));
+    AlignmentAnnotation anotherRnaWithDC = makeAnnot(new Annotation("B",
+            "", '<', 0f));
+    
+    // check self equivalence
+    for (boolean allowNull : new boolean[] { true, false })
+    {
+      assertEqualSecondaryStructure("Should be equal", one, anotherOne,
+              allowNull);
+      assertEqualSecondaryStructure("Should be equal", sheet, anotherSheet,
+              allowNull);
+      assertEqualSecondaryStructure("Should be equal", sheetWithLabel,
+              anotherSheetWithLabel, allowNull);
+      assertEqualSecondaryStructure("Should be equal", rnaNoDC,
+              anotherRnaNoDC, allowNull);
+      assertEqualSecondaryStructure("Should be equal", rnaWithDC,
+              anotherRnaWithDC, allowNull);
+      // display character doesn't matter for RNA structure (for 2.10.2)
+      assertEqualSecondaryStructure("Should be equal", rnaWithDC, rnaNoDC,
+              allowNull);
+      assertEqualSecondaryStructure("Should be equal", rnaNoDC, rnaWithDC,
+              allowNull);
+    }
+
+    // verify others are different
+    List<AlignmentAnnotation> aaSet = Arrays.asList(one, sheet,
+            sheetWithLabel, rnaWithDC);
+    for (int p = 0; p < aaSet.size(); p++)
+    {
+      for (int q = 0; q < aaSet.size(); q++)
+      {
+        if (p != q)
+        {
+          assertNotEqualSecondaryStructure("Should be different",
+                    aaSet.get(p), aaSet.get(q), false);
+        }
+        else
+        {
+          assertEqualSecondaryStructure("Should be same", aaSet.get(p),
+                  aaSet.get(q), false);
+          assertEqualSecondaryStructure("Should be same", aaSet.get(p),
+                  aaSet.get(q), true);
+          assertNotEqualSecondaryStructure(
+                  "Should be different to empty anot", aaSet.get(p),
+                  makeAnnot(Annotation.EMPTY_ANNOTATION), false);
+          assertNotEqualSecondaryStructure(
+                  "Should be different to empty annot",
+                  makeAnnot(Annotation.EMPTY_ANNOTATION), aaSet.get(q),
+                  true);
+          assertNotEqualSecondaryStructure("Should be different to null",
+                  aaSet.get(p), makeAnnot(null), false);
+          assertNotEqualSecondaryStructure("Should be different to null",
+                  makeAnnot(null), aaSet.get(q), true);
+        }
       }
     }
+
+    // test null
+
+  }
+
+  String aliFile = ">Dm\nAAACCCUUUUACACACGGGAAAGGG";
+  String annFile = "JALVIEW_ANNOTATION\n# Created: Thu May 04 11:16:52 BST 2017\n\n"
+          + "SEQUENCE_REF\tDm\nNO_GRAPH\tsecondary structure\tsecondary structure\t"
+          + "(|(|(|(|, .|, .|, .|, .|)|)|)|)|\t0.0\nROWPROPERTIES\t"
+          + "secondary structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false";
+
+  String annFileCurlyWuss = "JALVIEW_ANNOTATION\n# Created: Thu May 04 11:16:52 BST 2017\n\n"
+          + "SEQUENCE_REF\tDm\nNO_GRAPH\tsecondary structure\tsecondary structure\t"
+          + "(|(|(|(||{|{||{|{||)|)|)|)||}|}|}|}|\t0.0\nROWPROPERTIES\t"
+          + "secondary structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false";
+  String annFileFullWuss = "JALVIEW_ANNOTATION\n# Created: Thu May 04 11:16:52 BST 2017\n\n"
+          + "SEQUENCE_REF\tDm\nNO_GRAPH\tsecondary structure\tsecondary structure\t"
+          + "(|(|(|(||{|{||[|[||)|)|)|)||}|}|]|]|\t0.0\nROWPROPERTIES\t"
+          + "secondary structure\tscaletofit=true\tshowalllabs=true\tcentrelabs=false";
+
+  @Test(groups = { "Functional" })
+  public void secondaryStructureForRNASequence() throws Exception
+  {
+    roundTripSSForRNA(aliFile, annFile);
+  }
+
+  @Test(groups = { "Functional" })
+  public void curlyWUSSsecondaryStructureForRNASequence() throws Exception
+  {
+    roundTripSSForRNA(aliFile, annFileCurlyWuss);
+  }
+
+  @Test(groups = { "Functional" })
+  public void fullWUSSsecondaryStructureForRNASequence() throws Exception
+  {
+    roundTripSSForRNA(aliFile, annFileFullWuss);
+  }
+
+  @Test(groups = { "Functional" })
+  public void detectWussBrackets()
+  {
+    for (char ch : new char[] { '{', '}', '[', ']', '(', ')', '<', '>' })
+    {
+      Assert.assertTrue(StockholmFile.DETECT_BRACKETS.matchAt("" + ch, 0),
+              "Didn't recognise " + ch + " as a WUSS bracket");
+    }
+    for (char ch : new char[] { '@', '!', 'V', 'Q', '*', ' ', '-', '.' })
+    {
+      Assert.assertFalse(StockholmFile.DETECT_BRACKETS.matchAt("" + ch, 0),
+              "Shouldn't recognise " + ch + " as a WUSS bracket");
+    }
+  }
+  private static void roundTripSSForRNA(String aliFile, String annFile)
+          throws Exception
+  {
+    AlignmentI al = new AppletFormatAdapter().readFile(aliFile,
+            DataSourceType.PASTE, jalview.io.FileFormat.Fasta);
+    AnnotationFile aaf = new AnnotationFile();
+    aaf.readAnnotationFile(al, annFile, DataSourceType.PASTE);
+    al.getAlignmentAnnotation()[0].visible = true;
+
+    // TODO: create a better 'save as <format>' pattern
+    StockholmFile sf = new StockholmFile(al);
+
+    String stockholmFile = sf.print(al.getSequencesArray(), true);
+
+    AlignmentI newAl = new AppletFormatAdapter().readFile(stockholmFile,
+            DataSourceType.PASTE, jalview.io.FileFormat.Stockholm);
+    // AlignmentUtils.showOrHideSequenceAnnotations(newAl.getViewport()
+    // .getAlignment(), Arrays.asList("Secondary Structure"), newAl
+    // .getViewport().getAlignment().getSequences(), true, true);
+    testAlignmentEquivalence(al, newAl, true, true, true);
+
   }
 }