Merge branch 'develop' into efficiency/JAL-2034_JAL-1421
authorJim Procter <jprocter@issues.jalview.org>
Mon, 30 May 2016 12:20:53 +0000 (13:20 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Mon, 30 May 2016 12:20:53 +0000 (13:20 +0100)
189 files changed:
.classpath
THIRDPARTYLIBS
appletlib/plugin.jar [deleted file]
build.xml
doc/AddingGroovySupport.html
doc/building.html
examples/exampleFeatures.txt
examples/groovy/featureCounter.groovy [new file with mode: 0644]
examples/groovy/multipleFeatureAnnotations.groovy [new file with mode: 0644]
examples/plantfdx.features
examples/testdata/simpleGff3.gff [new file with mode: 0644]
examples/testdata/test_fts_data_columns.conf [new file with mode: 0644]
help/helpTOC.xml
help/html/features/annotationsFormat.html
help/html/features/featuresettings.html
help/html/features/groovy.html
help/html/features/mmcif.html [new file with mode: 0644]
help/html/features/seqmappings.html
help/html/features/sifts_mapping_output.png [new file with mode: 0644]
help/html/features/siftsmapping.html [new file with mode: 0644]
help/html/features/splitView.html
help/html/features/uniprotqueryfields.html [new file with mode: 0644]
help/html/features/uniprotseqfetcher.png [new file with mode: 0644]
help/html/features/uniprotsequencefetcher.html [new file with mode: 0644]
help/html/features/viewingpdbs.html
help/html/keys.html
help/html/menus/alwcalculate.html
help/html/menus/desktopMenu.html
help/html/menus/popupMenu.html
help/html/releases.html
help/html/webServices/AACon.html
help/html/webServices/index.html
help/html/webServices/msaclient.html
help/html/webServices/urllinks.html
lib/castor-1.1-cycle-xml.jar
lib/groovy-all-1.8.2.jar [deleted file]
lib/groovy-all-2.4.6-indy.jar [new file with mode: 0644]
lib/min-jabaws-client-2.1.0.jar
lib/quaqua-filechooser-only-8.0.jar
lib/xercesImpl.jar [changed mode: 0755->0644]
resources/fts/pdb_data_columns.txt [new file with mode: 0644]
resources/fts/uniprot_data_columns.txt [new file with mode: 0644]
resources/images/blank_16x16_placeholder.png [new file with mode: 0644]
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/MCview/PDBChain.java
src/MCview/PDBViewer.java
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/Conservation.java
src/jalview/analysis/CrossRef.java
src/jalview/analysis/SeqsetUtils.java
src/jalview/analysis/scoremodels/FeatureScoreModel.java
src/jalview/api/AlignCalcWorkerI.java
src/jalview/api/AlignViewControllerI.java
src/jalview/api/DBRefEntryI.java
src/jalview/api/FeatureRenderer.java
src/jalview/api/SiftsClientI.java
src/jalview/appletgui/FeatureRenderer.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/IdCanvas.java
src/jalview/appletgui/SeqPanel.java
src/jalview/bin/Cache.java
src/jalview/bin/Jalview.java
src/jalview/controller/AlignViewController.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentI.java
src/jalview/datamodel/ColumnSelection.java
src/jalview/datamodel/DBRefSource.java
src/jalview/datamodel/SeqCigar.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceFeature.java
src/jalview/datamodel/SequenceI.java
src/jalview/ext/ensembl/EnsemblGene.java
src/jalview/ext/ensembl/EnsemblInfo.java [new file with mode: 0644]
src/jalview/ext/ensembl/EnsemblRestClient.java
src/jalview/ext/ensembl/EnsemblSeqProxy.java
src/jalview/ext/ensembl/EnsemblSequenceFetcher.java
src/jalview/fts/api/FTSData.java [new file with mode: 0644]
src/jalview/fts/api/FTSDataColumnI.java [new file with mode: 0644]
src/jalview/fts/api/FTSRestClientI.java [new file with mode: 0644]
src/jalview/fts/api/GFTSPanelI.java [new file with mode: 0644]
src/jalview/fts/core/DecimalFormatTableCellRenderer.java [new file with mode: 0644]
src/jalview/fts/core/FTSDataColumnPreferences.java [moved from src/jalview/jbgui/PDBDocFieldPreferences.java with 60% similarity]
src/jalview/fts/core/FTSRestClient.java [new file with mode: 0644]
src/jalview/fts/core/FTSRestRequest.java [moved from src/jalview/ws/uimodel/PDBRestRequest.java with 79% similarity]
src/jalview/fts/core/FTSRestResponse.java [new file with mode: 0644]
src/jalview/fts/core/GFTSPanel.java [new file with mode: 0644]
src/jalview/fts/service/pdb/PDBFTSPanel.java [new file with mode: 0644]
src/jalview/fts/service/pdb/PDBFTSRestClient.java [new file with mode: 0644]
src/jalview/fts/service/uniprot/UniProtFTSRestClient.java [new file with mode: 0644]
src/jalview/fts/service/uniprot/UniprotFTSPanel.java [new file with mode: 0644]
src/jalview/gui/AlignFrame.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/CutAndPasteHtmlTransfer.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/gui/DasSourceBrowser.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/IdPanel.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/OptsAndParamsPage.java
src/jalview/gui/PDBSearchPanel.java [deleted file]
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceFetcher.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/TreeCanvas.java
src/jalview/gui/TreePanel.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/FeaturesFile.java
src/jalview/io/HtmlSvgOutput.java
src/jalview/io/IdentifyFile.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/io/StructureFile.java
src/jalview/io/gff/GffConstants.java [new file with mode: 0644]
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GPDBSearchPanel.java [deleted file]
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GStructureChooser.java
src/jalview/renderer/ScaleRenderer.java [new file with mode: 0644]
src/jalview/renderer/seqfeatures/FeatureRenderer.java
src/jalview/util/ColorUtils.java
src/jalview/util/Comparison.java
src/jalview/util/DnaUtils.java [new file with mode: 0644]
src/jalview/util/ImageMaker.java
src/jalview/util/StringUtils.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/workers/AlignCalcWorker.java
src/jalview/workers/AlignmentAnnotationFactory.java [new file with mode: 0644]
src/jalview/workers/AnnotationProviderI.java [new file with mode: 0644]
src/jalview/workers/AnnotationWorker.java [new file with mode: 0644]
src/jalview/workers/ColumnCounterWorker.java [new file with mode: 0644]
src/jalview/workers/ConsensusThread.java
src/jalview/workers/ConservationThread.java
src/jalview/workers/FeatureCounterI.java [new file with mode: 0644]
src/jalview/workers/StrucConsensusThread.java
src/jalview/ws/DBRefFetcher.java
src/jalview/ws/DasSequenceFeatureFetcher.java
src/jalview/ws/SequenceFetcher.java
src/jalview/ws/dbsources/PDBRestClient.java [deleted file]
src/jalview/ws/dbsources/Pdb.java
src/jalview/ws/dbsources/PfamFull.java
src/jalview/ws/dbsources/PfamSeed.java
src/jalview/ws/dbsources/RfamFull.java
src/jalview/ws/dbsources/RfamSeed.java
src/jalview/ws/dbsources/Xfam.java
src/jalview/ws/jws2/AADisorderClient.java
src/jalview/ws/jws2/JPred301Client.java
src/jalview/ws/jws2/Jws2Client.java
src/jalview/ws/jws2/MsaWSClient.java
src/jalview/ws/jws2/RNAalifoldClient.java
src/jalview/ws/uimodel/PDBRestResponse.java [deleted file]
test/MCview/PDBChainTest.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/analysis/SeqsetUtilsTest.java [new file with mode: 0644]
test/jalview/analysis/scoremodels/FeatureScoreModelTest.java
test/jalview/datamodel/AlignmentTest.java
test/jalview/datamodel/ColumnSelectionTest.java
test/jalview/datamodel/SeqCigarTest.java
test/jalview/datamodel/SequenceTest.java
test/jalview/ext/ensembl/EnsemblRestClientTest.java
test/jalview/ext/ensembl/EnsemblSeqProxyTest.java
test/jalview/ext/ensembl/EnsemblXrefTest.java
test/jalview/ext/jmol/JmolParserTest.java
test/jalview/fts/core/FTSRestClientTest.java [new file with mode: 0644]
test/jalview/fts/service/pdb/PDBFTSPanelTest.java [moved from test/jalview/gui/PDBSearchPanelTest.java with 79% similarity]
test/jalview/fts/service/pdb/PDBFTSRestClientTest.java [moved from test/jalview/ws/dbsources/PDBRestClientTest.java with 59% similarity]
test/jalview/gui/AlignFrameTest.java [new file with mode: 0644]
test/jalview/gui/StructureChooserTest.java
test/jalview/io/FeaturesFileTest.java
test/jalview/io/IdentifyFileTest.java
test/jalview/io/Jalview2xmlTests.java
test/jalview/io/SequenceAnnotationReportTest.java [new file with mode: 0644]
test/jalview/structure/Mapping.java
test/jalview/structure/StructureSelectionManagerTest.java
test/jalview/util/ColorUtilsTest.java
test/jalview/util/ComparisonTest.java
test/jalview/util/DnaUtilsTest.java [new file with mode: 0644]
test/jalview/util/StringUtilsTest.java
test/jalview/ws/dbsources/XfamFetcherTest.java [new file with mode: 0644]
test/jalview/ws/jabaws/DisorderAnnotExportImport.java
utils/InstallAnywhere/Jalview.iap_xml

index cad9e2b..6583992 100644 (file)
@@ -39,7 +39,6 @@
        <classpathentry kind="lib" path="lib/jdas-1.0.4.jar"/>
        <classpathentry kind="lib" path="lib/spring-core-3.0.5.RELEASE.jar"/>
        <classpathentry kind="lib" path="lib/spring-web-3.0.5.RELEASE.jar"/>
-       <classpathentry kind="lib" path="lib/groovy-all-1.8.2.jar"/>
        <classpathentry kind="lib" path="lib/min-jabaws-client-2.1.0.jar" sourcepath="/clustengine"/>
        <classpathentry kind="lib" path="lib/json_simple-1.1.jar" sourcepath="/Users/jimp/Downloads/json_simple-1.1-all.zip"/>
        <classpathentry kind="lib" path="lib/slf4j-api-1.7.7.jar"/>
@@ -69,5 +68,6 @@
        <classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
        <classpathentry kind="lib" path="lib/biojava-core-4.1.0.jar"/>
        <classpathentry kind="lib" path="lib/biojava-ontology-4.1.0.jar"/>
+       <classpathentry kind="lib" path="lib/groovy-all-2.4.6-indy.jar"/>
        <classpathentry kind="output" path="classes"/>
 </classpath>
index c6c817a..caaa7f9 100644 (file)
@@ -21,6 +21,7 @@ commons-discovery.jar
 commons-logging-1.1.1.jar
 commons-logging.jar
 commons-net-3.3.jar
+groovy-all-2.4.6-indy.jar      APL 2.0 License - downloaded and extracted from https://dl.bintray.com/groovy/maven/apache-groovy-binary-2.4.6.zip
 httpclient-4.0.3.jar
 httpcore-4.0.1.jar
 httpmime-4.0.3.jar
diff --git a/appletlib/plugin.jar b/appletlib/plugin.jar
deleted file mode 100644 (file)
index d19dcc5..0000000
Binary files a/appletlib/plugin.jar and /dev/null differ
index b23293f..a8b8928 100755 (executable)
--- a/build.xml
+++ b/build.xml
@@ -27,7 +27,9 @@
     <echo message="build - compiles all necessary files for Application" />
     <echo message="makedist - compiles and places all necessary jar files into directory dist" />
     <echo message="makefulldist - signs all jar files and builds jnlp file for full distribution" />
-    <echo message="              this needs a keystore and key. Add -Dtimestamp to timestamp signed jars"/>
+    <echo message="              this needs a keystore and key."/>
+    <echo message="              Add -Dtimestamp to timestamp signed jars"/>
+    <echo message="              -Djalview.keyalg and -Djalview.keydig are SHA1/SHA1withRSA"/>
     <echo message="              See docs/building.html for more information." />
     <echo message="compileApplet - compiles all necessary files for Applet" />
     <echo message="makeApplet - compiles, then packages and obfuscates the Applet" />
@@ -88,6 +90,9 @@
     <!-- locally valid proxy for signing with external time server -->
     <property name="proxyPort" value="80"/>
     <property name="proxyHost" value="sqid"/>
+    <!-- key sign/digest algorithms -->
+    <property name="jalview.keyalg" value="SHA1withRSA" description="key algorithm for signing"/>
+    <property name="jalview.keydig" value="SHA1" description="algorithm for jar digest"/>
 
     <!-- default TestNG groups to run -->
     <property name="testng-groups" value="Functional" />
   </target>
 
   <target name="-jarsignwithtsa" depends="makedist,preparejnlp" if="timestamp">
-    <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false" sigalg="SHA1withRSA"
+    <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false" sigalg="${jalview.keyalg}" digestalg="${jalview.keydig}"
       tsaproxyhost="${proxyHost}" tsaproxyport="${proxyPort}" tsaurl="${jalview.tsaurl}">
       <fileset dir="${packageDir}">
         <include name="*.jar" />
     </signjar>
   </target>
   <target name="-jarsignnotsa" depends="makedist,preparejnlp" unless="timestamp">
-    <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false" sigalg="SHA1withRSA">
+    <signjar storepass="${jalview.keystore.pass}" keypass="${jalview.key.pass}" keystore="${jalview.keystore}" alias="${jalview.key}" lazy="false" verbose="false" sigalg="${jalview.keyalg}" digestalg="${jalview.keydig}">
       <fileset dir="${packageDir}">
         <include name="*.jar" />
       </fileset>
         </information>
         <resources>
           <j2se version="${j2sev}" initial_heap_size="${inih}" max_heap_size="${maxh}" />
+          <jar main="true" href="jalview.jar"/>
           <fileset dir="${packageDir}">
-            <include name="jalview.jar" />
-          </fileset>
-          <fileset dir="${packageDir}">
+            <exclude name="jalview.jar" />
             <include name="*.jar" />
             <include name="*_*.jar" />
-            <exclude name="jalview.jar" />
             <exclude name="*jnilib.jar" />
           </fileset>
           <property name="jalview.version" value="${JALVIEW_VERSION}" />
     <pathelement location="appletlib/${jsoup}" />
     <pathelement location="appletlib/${jsonSimple}" />
     <pathelement location="appletlib/${javaJson}" />
-    <pathelement location="appletlib/plugin.jar" />
+       <fileset dir="${java.home}/lib">
+        <include name="plugin.jar" />
+    </fileset>
   </path>
   <taskdef resource="proguard/ant/task.properties" classpath="utils/proguard.jar" />
 
index 41e34ce..e3e453f 100644 (file)
 </title>
 <body>
 <h1>
-Adding Groovy Support to Jalview
+Groovy Support in Jalview
 </h1>
 <p>
-There is currently no scripting language 
-extension within Jalview, in part because a 
-scripting API has not yet been developed.
-</p>
-<p>It is, however, really easy to embed scripting
-engines like groovy. If groovy is detected on the 
-classpath, a new menu entry on the Desktop's Tools 
-menu will open the GroovyShell.
+  <a href="http://www.groovy-lang.org">Groovy</a> has been bundled with the Jalview desktop since circa 2012. The program supports interactive execution of groovy scripts via the Groovy Console, and command line execution via the '-groovy' option. The main source for documentation about Groovy in Jalview is the <a href="http://www.jalview.org/help/html/features/groovy.html">online help pages</a>.
 </p>
 <p>Here are some scripts to get you started:</p>
 <ul><li>Getting the title, alignment and first sequence from the current alignFrame<br>
 <pre>
-def alf = Jalview.getAlignframes();
+def alf = Jalview.getAlignFrames();
 print alf[0].getTitle();
 def alignment = alf[0].viewport.alignment;
 def seq = alignment.getSequenceAt(0);
 </pre>
 </li>
 </ul>
-<h1>Getting Groovy...</h1>
-<p>
-You need the core groovy jars which include the GroovyShell. The easiest way of doing
-this is to add the groovy-all-*.jar to the lib directory whose path is given in the java.ext.dirs property.</p>
-<p>The is obtained from the <em>embedded</em> directory within the <a 
-href="http://dist.codehaus.org/groovy/distributions"/>groovy distribution</a>).
-</p>
 <h2>TODO</h2>
 <p>
 Using Java class methods from Groovy is straightforward, but currently, there isn't a set of easy to use methods for the jalview objects. A Jalview Scripting API needs to be developed to make this easier.</p>
index 3a39691..ecde4d8 100755 (executable)
 <p>
 You will need the following (hopefully):<br>
 <ul>
-<li>Java development kit (JDK1.6 is the recommended platform for developing with Jalview, although JDK1.7 seems to work too!).</li>
-<li>Ant (we think 1.5.4 is quite sufficient to use the simple build
-file supplied, and it seems to work with later versions e.g. 1.7).</li>
+<li>Java development kit (JDK1.8 is now the recommended platform for developing with Jalview.</li>
+<li>Ant (1.7 or later will be needed for some of the jarsigning tools).</li>
 </ul>
 With any luck, after setting your paths and JAVA_HOME correctly, you
 just need to change to the Jalview directory and run ant (this works
-from JBuilder and eclipse too, but NetBeans is a bit trickier).
+from eclipse too, but NetBeans is a bit trickier).
 <pre>
    ant
 </pre>
@@ -46,23 +45,24 @@ build target in ant to make the signed jar files in a directory called
 dist. But first you need to make your own key:
 <p><strong>Making your own key</strong></p>
 
-<p>The ant 'makefulldist' target assumes that a keystore exists in a
-directory 'keys'. To make a key accessible using the default settings
-in the build.xml file then make the keys directory and add the
-jarsigner key with the following :
-</p>
-<pre>
-mkdir keys
-keytool -genkey -keystore keys/.keystore -keypass alignmentisfun
--storepass alignmentisfun -alias jalview
- (you will have to answer some personal questions here)
-ant makedist
- (should eventually generate a Jalview.jnlp file
-  in ./dist along with a set of signed jars using the jalview
-  key)
-</pre>
-
-       <p>
+  <p>The ant 'makefulldist' target assumes that a keystore exists in
+    a directory 'keys'. To make a key accessible using the default
+    settings in the build.xml file then make the keys directory and add
+    the jarsigner key with the following :</p>
+  <pre>mkdir keys</pre>
+  <pre>keytool -genkey -keystore keys/.keystore -keypass alignmentisfun
+  -storepass alignmentisfun -sigalg SHA1withRSA -keyalg RSA -alias jalview</pre>
+  <em>(you will have to answer some personal questions here)</em>
+  <pre>ant makedist -DWebStartLocation="file://.pathtojalviewsource./dist" -Dapplication.codebase="*"</pre>
+  <p>This should eventually generate a jalview.jnlp file in ./dist
+    along with a set of signed jars using the jalview key). In order to
+    test locally via webstart you'll now need to add 'file:/' to your
+    java webstart security exception list. Then:</p>
+  <pre>javaws file://.pathtojalviewsource./dist/jalview.jnlp</pre>
+  <p>Please remember to remove that entry afterwards, since it will leave
+  your system vulnerable to malicious code.
+  </p>
+  <p>
                <strong>Building the JalviewLite applet<br>
                </strong> The JalviewLite applet is compiled using a subset of the packages in
                the src directory (specifically: MCView, and jalview.{datamodel,
index dfadb50..c0098a9 100755 (executable)
@@ -1,5 +1,5 @@
 ST-TURN-IIL    blue|255,0,255|absolute|20.0|95.0|below|66.0
-GAMMA-TURN-CLASSIC             red|0,255,255|20.0|95.0|below|66.0
+GAMMA-TURN-CLASSIC     red|0,255,255|20.0|95.0|below|66.0
 BETA-TURN-IR   9a6a94
 BETA-TURN-IL   d6a6ca
 BETA-BULGE     1dc451
@@ -26,73 +26,74 @@ BETA-TURN-IIL       8b5b50
 ST-MOTIF       ac25a1
 
 STARTGROUP     uniprot
+<html><a href="http://pfam.xfam.org/family/PF00111">Pfam family</a></html>     FER_CAPAA       -1      0       0       Pfam
 Iron-sulfur (2Fe-2S)   FER_CAPAA       -1      39      39      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAA       -1      44      44      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAA       -1      47      47      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAA       -1      77      77      METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 8_8</a></html> FER_CAPAA       -1      8       83      Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 8_8</a></html>     FER_CAPAA       -1      8       83      Pfam
 Ferredoxin_fold Status: True Positive  FER_CAPAA       -1      3       93      Cath
 Iron-sulfur (2Fe-2S)   FER_CAPAN       -1      86      86      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAN       -1      91      91      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAN       -1      94      94      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAN       -1      124     124     METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 55_13</a></html>       FER_CAPAN       -1      55      130     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 55_13</a></html>   FER_CAPAN       -1      55      130     Pfam
 Ferredoxin_fold Status: True Positive  FER_CAPAN       -1      45      140     Cath
 Iron-sulfur (2Fe-2S)   FER1_SOLLC      -1      86      86      METAL
 Iron-sulfur (2Fe-2S)   FER1_SOLLC      -1      91      91      METAL
 Iron-sulfur (2Fe-2S)   FER1_SOLLC      -1      94      94      METAL
 Iron-sulfur (2Fe-2S)   FER1_SOLLC      -1      124     124     METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 55_13</a></html>       FER1_SOLLC      -1      55      130     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 55_13</a></html>   FER1_SOLLC      -1      55      130     Pfam
 Ferredoxin_fold Status: True Positive  FER1_SOLLC      -1      45      140     Cath
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 55_13</a></html>       Q93XJ9_SOLTU    -1      55      130     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 55_13</a></html>   Q93XJ9_SOLTU    -1      55      130     Pfam
 Ferredoxin_fold Status: True Positive  Q93XJ9_SOLTU    -1      45      140     Cath
 Iron-sulfur (2Fe-2S)   FER1_PEA        -1      91      91      METAL
 Iron-sulfur (2Fe-2S)   FER1_PEA        -1      96      96      METAL
 Iron-sulfur (2Fe-2S)   FER1_PEA        -1      99      99      METAL
 Iron-sulfur (2Fe-2S)   FER1_PEA        -1      129     129     METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_13</a></html>       FER1_PEA        -1      60      135     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_13</a></html>   FER1_PEA        -1      60      135     Pfam
 Ferredoxin_fold Status: True Positive  FER1_PEA        -1      50      145     Cath
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 63_13</a></html>       Q7XA98_TRIPR    -1      63      138     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 63_13</a></html>   Q7XA98_TRIPR    -1      63      138     Pfam
 Ferredoxin_fold Status: True Positive  Q7XA98_TRIPR    -1      53      148     Cath
 Iron-sulfur (2Fe-2S)   FER1_MESCR      -1      90      90      METAL
 Iron-sulfur (2Fe-2S)   FER1_MESCR      -1      95      95      METAL
 Iron-sulfur (2Fe-2S)   FER1_MESCR      -1      98      98      METAL
 Iron-sulfur (2Fe-2S)   FER1_MESCR      -1      128     128     METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 59_13</a></html>       FER1_MESCR      -1      59      134     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 59_13</a></html>   FER1_MESCR      -1      59      134     Pfam
 Ferredoxin_fold Status: True Positive  FER1_MESCR      -1      49      144     Cath
 Iron-sulfur (2Fe-2S)   FER1_SPIOL      -1      89      89      METAL
 Iron-sulfur (2Fe-2S)   FER1_SPIOL      -1      94      94      METAL
 Iron-sulfur (2Fe-2S)   FER1_SPIOL      -1      97      97      METAL
 Iron-sulfur (2Fe-2S)   FER1_SPIOL      -1      127     127     METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 58_13</a></html>       FER1_SPIOL      -1      58      133     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 58_13</a></html>   FER1_SPIOL      -1      58      133     Pfam
 Ferredoxin_fold Status: True Positive  FER1_SPIOL      -1      48      143     Cath
 Iron-sulfur (2Fe-2S)   FER3_RAPSA      -1      39      39      METAL
 Iron-sulfur (2Fe-2S)   FER3_RAPSA      -1      44      44      METAL
 Iron-sulfur (2Fe-2S)   FER3_RAPSA      -1      47      47      METAL
 Iron-sulfur (2Fe-2S)   FER3_RAPSA      -1      77      77      METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 8_8</a></html> FER3_RAPSA      -1      8       83      Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 8_8</a></html>     FER3_RAPSA      -1      8       83      Pfam
 Ferredoxin_fold Status: True Positive  FER3_RAPSA      -1      3       93      Cath
 Iron-sulfur (2Fe-2S)   FER_BRANA       -1      39      39      METAL
 Iron-sulfur (2Fe-2S)   FER_BRANA       -1      44      44      METAL
 Iron-sulfur (2Fe-2S)   FER_BRANA       -1      47      47      METAL
 Iron-sulfur (2Fe-2S)   FER_BRANA       -1      77      77      METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 8_8</a></html> FER_BRANA       -1      8       83      Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 8_8</a></html>     FER_BRANA       -1      8       83      Pfam
 Ferredoxin_fold Status: True Positive  FER_BRANA       -1      2       96      Cath
 Iron-sulfur (2Fe-2S)   FER2_ARATH      -1      91      91      METAL
 Iron-sulfur (2Fe-2S)   FER2_ARATH      -1      96      96      METAL
 Iron-sulfur (2Fe-2S)   FER2_ARATH      -1      99      99      METAL
 Iron-sulfur (2Fe-2S)   FER2_ARATH      -1      129     129     METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_13</a></html>       FER2_ARATH      -1      60      135     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_13</a></html>   FER2_ARATH      -1      60      135     Pfam
 Ferredoxin_fold Status: True Positive  FER2_ARATH      -1      50      145     Cath
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_11</a></html>       Q93Z60_ARATH    -1      60      118     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_11</a></html>   Q93Z60_ARATH    -1      60      118     Pfam
 Ferredoxin_fold Status: True Positive  Q93Z60_ARATH    -1      52      118     Cath
 Iron-sulfur (2Fe-2S)   FER1_MAIZE      -1      91      91      METAL
 Iron-sulfur (2Fe-2S)   FER1_MAIZE      -1      96      96      METAL
 Iron-sulfur (2Fe-2S)   FER1_MAIZE      -1      99      99      METAL
 Iron-sulfur (2Fe-2S)   FER1_MAIZE      -1      129     129     METAL
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_13</a></html>       FER1_MAIZE      -1      60      135     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_13</a></html>   FER1_MAIZE      -1      60      135     Pfam
 Ferredoxin_fold Status: True Positive  FER1_MAIZE      -1      50      145     Cath
-<html>Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 52_12</a></html>       O80429_MAIZE    -1      52      127     Pfam
+<html>Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 52_12</a></html>   O80429_MAIZE    -1      52      127     Pfam
 Ferredoxin_fold Status: True Positive  O80429_MAIZE    -1      42      137     Cath
 ENDGROUP       uniprot
 
diff --git a/examples/groovy/featureCounter.groovy b/examples/groovy/featureCounter.groovy
new file mode 100644 (file)
index 0000000..42d3187
--- /dev/null
@@ -0,0 +1,90 @@
+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 this, first load uniref50.fa from the examples folder, then load features
+ * from examples/exampleFeatures.txt, before running this script from the Groovy console.
+ * Modify this example as required to count by column any desired value that can be 
+ * derived from the residue and sequence features at each position of 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 counts residues with a Pfam feature annotation
+ * 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 that counts any residue with Pfam domain annotation
+ */
+def pfamAnnotation = getColumnCounter("Pfam", "Count of residues with Pfam domain annotation", {true}, hasPfam)
+
+/*
+ * Define an annotation 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/multipleFeatureAnnotations.groovy b/examples/groovy/multipleFeatureAnnotations.groovy
new file mode 100644 (file)
index 0000000..592c7f5
--- /dev/null
@@ -0,0 +1,110 @@
+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) 
index a23d152..872dadc 100644 (file)
@@ -14,22 +14,22 @@ Iron-sulfur (2Fe-2S)        FER_CAPAA       -1      39      39      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAA       -1      44      44      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAA       -1      47      47      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAA       -1      77      77      METAL
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 8_83</a></html>   FER_CAPAA       -1      8       83      Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 8_83</a></html>       FER_CAPAA       -1      8       83      Pfam
 Chloroplast    FER_CAPAN       -1      1       47      TRANSIT
 Iron-sulfur (2Fe-2S)   FER_CAPAN       -1      86      86      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAN       -1      91      91      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAN       -1      94      94      METAL
 Iron-sulfur (2Fe-2S)   FER_CAPAN       -1      124     124     METAL
 Phosphothreonine       FER_CAPAN       -1      136     136     MOD_RES
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 55_130</a></html> FER_CAPAN       -1      55      130     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 55_130</a></html>     FER_CAPAN       -1      55      130     Pfam
 Chloroplast    FER1_SOLLC      -1      1       47      TRANSIT
 Iron-sulfur (2Fe-2S)   FER1_SOLLC      -1      86      86      METAL
 Iron-sulfur (2Fe-2S)   FER1_SOLLC      -1      91      91      METAL
 Iron-sulfur (2Fe-2S)   FER1_SOLLC      -1      94      94      METAL
 Iron-sulfur (2Fe-2S)   FER1_SOLLC      -1      124     124     METAL
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 55_130</a></html> FER1_SOLLC      -1      55      130     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 55_130</a></html>     FER1_SOLLC      -1      55      130     Pfam
 Evidence: EI4  Q93XJ9_SOLTU    -1      1       48      SIGNAL
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 55_130</a></html> Q93XJ9_SOLTU    -1      55      130     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 55_130</a></html>     Q93XJ9_SOLTU    -1      55      130     Pfam
 Chloroplast    FER1_PEA        -1      1       52      TRANSIT
 L -> I (in strain: cv. Onward)         FER1_PEA        -1      59      59      VARIANT
 I -> L (in strain: cv. Onward)         FER1_PEA        -1      85      85      VARIANT
@@ -38,14 +38,14 @@ Iron-sulfur (2Fe-2S)        FER1_PEA        -1      96      96      METAL
 Iron-sulfur (2Fe-2S)   FER1_PEA        -1      99      99      METAL
 Iron-sulfur (2Fe-2S)   FER1_PEA        -1      129     129     METAL
 YPTS -> PPPA (in Ref. 2)       FER1_PEA        -1      132     135     CONFLICT
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_135</a></html> FER1_PEA        -1      60      135     Pfam
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 63_138</a></html> Q7XA98_TRIPR    -1      63      138     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_135</a></html>     FER1_PEA        -1      60      135     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 63_138</a></html>     Q7XA98_TRIPR    -1      63      138     Pfam
 Chloroplast    FER1_MESCR      -1      1       51      TRANSIT
 Iron-sulfur (2Fe-2S)   FER1_MESCR      -1      90      90      METAL
 Iron-sulfur (2Fe-2S)   FER1_MESCR      -1      95      95      METAL
 Iron-sulfur (2Fe-2S)   FER1_MESCR      -1      98      98      METAL
 Iron-sulfur (2Fe-2S)   FER1_MESCR      -1      128     128     METAL
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 59_134</a></html> FER1_MESCR      -1      59      134     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 59_134</a></html>     FER1_MESCR      -1      59      134     Pfam
 Chloroplast    FER1_SPIOL      -1      1       50      TRANSIT
 STRAND FER1_SPIOL      -1      52      59      STRAND
 TURN   FER1_SPIOL      -1      60      61      TURN
@@ -68,7 +68,7 @@ Iron-sulfur (2Fe-2S)  FER1_SPIOL      -1      127     127     METAL
 STRAND FER1_SPIOL      -1      130     133     STRAND
 STRAND FER1_SPIOL      -1      135     138     STRAND
 HELIX  FER1_SPIOL      -1      142     144     HELIX
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 58_133</a></html> FER1_SPIOL      -1      58      133     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 58_133</a></html>     FER1_SPIOL      -1      58      133     Pfam
 I -> V         FER3_RAPSA      -1      8       8       VARIANT
 Iron-sulfur (2Fe-2S)   FER3_RAPSA      -1      39      39      METAL
 Iron-sulfur (2Fe-2S)   FER3_RAPSA      -1      44      44      METAL
@@ -77,25 +77,25 @@ S -> T      FER3_RAPSA      -1      55      55      VARIANT
 Iron-sulfur (2Fe-2S)   FER3_RAPSA      -1      77      77      METAL
 R -> K         FER3_RAPSA      -1      91      91      VARIANT
 M -> V         FER3_RAPSA      -1      95      95      VARIANT
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 8_83</a></html>   FER3_RAPSA      -1      8       83      Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 8_83</a></html>       FER3_RAPSA      -1      8       83      Pfam
 Chloroplast    FER1_ARATH      -1      1       52      TRANSIT
 Iron-sulfur (2Fe-2S)   FER1_ARATH      -1      91      91      METAL
 Iron-sulfur (2Fe-2S)   FER1_ARATH      -1      96      96      METAL
 Iron-sulfur (2Fe-2S)   FER1_ARATH      -1      99      99      METAL
 Iron-sulfur (2Fe-2S)   FER1_ARATH      -1      129     129     METAL
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_135</a></html> FER1_ARATH      -1      60      135     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_135</a></html>     FER1_ARATH      -1      60      135     Pfam
 Iron-sulfur (2Fe-2S)   FER_BRANA       -1      39      39      METAL
 Iron-sulfur (2Fe-2S)   FER_BRANA       -1      44      44      METAL
 Iron-sulfur (2Fe-2S)   FER_BRANA       -1      47      47      METAL
 Iron-sulfur (2Fe-2S)   FER_BRANA       -1      77      77      METAL
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 8_83</a></html>   FER_BRANA       -1      8       83      Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 8_83</a></html>       FER_BRANA       -1      8       83      Pfam
 Chloroplast    FER2_ARATH      -1      1       52      TRANSIT
 Iron-sulfur (2Fe-2S)   FER2_ARATH      -1      91      91      METAL
 Iron-sulfur (2Fe-2S)   FER2_ARATH      -1      96      96      METAL
 Iron-sulfur (2Fe-2S)   FER2_ARATH      -1      99      99      METAL
 Iron-sulfur (2Fe-2S)   FER2_ARATH      -1      129     129     METAL
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_135</a></html> FER2_ARATH      -1      60      135     Pfam
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_118</a></html> Q93Z60_ARATH    -1      60      118     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_135</a></html>     FER2_ARATH      -1      60      135     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_118</a></html>     Q93Z60_ARATH    -1      60      118     Pfam
 Chloroplast    FER1_MAIZE      -1      1       52      TRANSIT
 STRAND FER1_MAIZE      -1      57      59      STRAND
 STRAND FER1_MAIZE      -1      72      74      STRAND
@@ -113,6 +113,6 @@ Iron-sulfur (2Fe-2S)        FER1_MAIZE      -1      129     129     METAL
 STRAND FER1_MAIZE      -1      132     135     STRAND
 STRAND FER1_MAIZE      -1      137     141     STRAND
 TURN   FER1_MAIZE      -1      142     142     TURN
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 60_135</a></html> FER1_MAIZE      -1      60      135     Pfam
-<html>Description: Fer2 Status: True Positive <a href="http://pfam.sanger.ac.uk/family/PF00111">Pfam 52_127</a></html> O80429_MAIZE    -1      52      127     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 60_135</a></html>     FER1_MAIZE      -1      60      135     Pfam
+<html>Description: Fer2 Status: True Positive <a href="http://pfam.xfam.org/family/PF00111">Pfam 52_127</a></html>     O80429_MAIZE    -1      52      127     Pfam
 ENDGROUP       uniprot
diff --git a/examples/testdata/simpleGff3.gff b/examples/testdata/simpleGff3.gff
new file mode 100644 (file)
index 0000000..34b64ee
--- /dev/null
@@ -0,0 +1,27 @@
+##gff-version 2
+# exonerate output in gff2 format; not gff3 because
+#   - 'similarity' is not a Sequence Ontology term
+#   - attributes' name/values are separated by space ' ' not equals '='
+##source-version exonerate:protein2genome:local 2.2.0
+##date 2015-01-16
+##type DNA
+#
+# exonerate run with --showtargetgff generates 'features on the target' i.e. mappings to the query
+# tab-delimited
+# seqname source feature start end score strand frame attributes
+#
+seq1   exonerate:protein2genome:local  gene    8       11      3652    -       .       gene_id 0 ; sequence seq2 ; gene_orientation .
+seq1   exonerate:protein2genome:local  cds     9       11      .       -       .       
+seq1   exonerate:protein2genome:local  exon    9       11      .       -       .       insertions 3 ; deletions 6
+#seq1  exonerate:protein2genome:local  similarity      8       11      3652    -       .       alignment_id 0 ; Query seq2 ; Align 11 1 3
+seq1   exonerate:protein2genome:local  similarity      9       11      3652    -       .       alignment_id 0 ; Query seq2 ; Align 11 1 3
+#
+# appending FASTA sequences is strictly a GFF3 format feature
+# but Jalview is able to handle this mixture of GFF2 / GFF3 :-)
+#
+##FASTA
+>seq1
+ACTACGACACGACGACGACGACG
+>seq2
+CDEQEATGTQDAQEQAQC
+
diff --git a/examples/testdata/test_fts_data_columns.conf b/examples/testdata/test_fts_data_columns.conf
new file mode 100644 (file)
index 0000000..10f5043
--- /dev/null
@@ -0,0 +1,153 @@
+uniprot_data_columns
+#
+_group.id
+_group.name
+_group.sort_order
+g1;Quality Measures;3
+g2;Cross References;2
+g3;Names & Taxonomy;1
+g4;Procedures & Softwares;4
+g5;Date Of;5
+g6;Miscellenous;6
+g7;Sequences;7
+g8;Function;8
+g9;Interaction;9
+g10;Expression;10
+g11;Gene Ontology;11
+g12;Pathology & Biotech;12
+g13;Subcellular location;13
+g14;PTM / Processing;14
+g15;Structure;15
+g16;Publications;16
+g17;Date of;17
+g18;Family & Domain;18
+#
+_data_column.primary_key;entry name
+_data_column.default_response_page_size;100
+#
+_data_column.name
+_data_column.code
+_data_column.group_id
+_data_column.data_type
+_data_column.min_col_width
+_data_column.max_col_width
+_data_column.preferred_col_width
+_data_column.is_shown_by_default
+_data_column.is_searchable
+Entry Name;entry name;String;g3;80;100;85;true;true
+Protein names;protein names;String;g3;300;1500;500;true;true
+Gene Names;genes;String;g3;50;1000;95;true;true
+Organism;organism;String;g3;50;1000;95;true;true
+Organism ID;organism-id;int;g3;60;100;80;false;true
+Proteomes;proteome;String;g3;50;1000;95;false;true
+Taxonomic lineage (ALL);lineage(ALL);String;g3;50;400;95;false;false
+Virus hosts;virus hosts;String;g3;50;1000;95;false;true
+Fragment;fragment;String;g7;50;1000;95;false;true
+Gene encoded by;encodedon;String;g7;50;1000;95;false;true
+Alternative products (isoforms);comment(ALTERNATIVE PRODUCTS);String;g7;50;1000;95;false;false
+Erroneous gene model prediction;comment(ERRONEOUS GENE MODEL PREDICTION);String;g7;50;1000;95;false;false
+Erroneous initiation;comment(ERRONEOUS INITIATION);String;g7;50;1000;95;false;false
+Erroneous translation;comment(ERRONEOUS TRANSLATION);String;g7;50;1000;95;false;false
+Frameshift;comment(FRAMESHIFT);String;g7;50;1000;95;false;false
+Mass spectrometry;comment(MASS SPECTROMETRY);String;g7;50;1000;95;false;false
+Polymorphism;comment(POLYMORPHISM);String;g7;50;1000;95;false;false
+RNA editing;comment(RNA EDITING);String;g7;50;1000;95;false;false
+Sequence caution;comment(SEQUENCE CAUTION);String;g7;50;1000;95;false;false
+Length;length;int;g7;50;100;95;false;true
+Mass;mass;String;g7;50;100;80;false;true
+Sequence;sequence;String;g7;50;1000;95;false;true
+Alternative sequence;feature(ALTERNATIVE SEQUENCE);String;g7;50;1000;95;false;false
+Natural variant;feature(NATURAL VARIANT);String;g7;50;1000;95;false;false
+Non-adjacent residues;feature(NON ADJACENT RESIDUES);String;g7;50;1000;95;false;false
+Non-standard residue;feature(NON STANDARD RESIDUE);String;g7;50;1000;95;false;false
+Non-terminal residue;feature(NON TERMINAL RESIDUE);String;g7;50;1000;95;false;false
+Sequence conflict;feature(SEQUENCE CONFLICT);String;g7;50;1000;95;false;false
+Sequence uncertainty;feature(SEQUENCE UNCERTAINTY);String;g7;50;1000;95;false;false
+Version (Sequence);version(sequence);String;g7;50;1000;95;false;false
+EC number;ec;String;g8;50;1000;95;false;true
+Absorption;comment(ABSORPTION);String;g8;50;1000;95;false;false
+Catalytic activity;comment(CATALYTIC ACTIVITY);String;g8;50;1000;95;false;false
+Cofactor;comment(COFACTOR);String;g8;50;1000;95;false;false
+Enzyme regulation;comment(ENZYME REGULATION);String;g8;50;1000;95;false;false
+Function [CC];comment(FUNCTION);String;g8;50;1000;95;false;false
+Kinetics;comment(KINETICS);String;g8;50;1000;95;false;false
+Pathway;comment(PATHWAY);String;g8;50;1000;95;false;false
+Redox potential;comment(REDOX POTENTIAL);String;g8;50;1000;95;false;false
+Temperature dependence;comment(TEMPERATURE DEPENDENCE);String;g8;50;1000;95;false;false
+pH dependence;comment(PH DEPENDENCE);String;g8;50;1000;95;false;false
+Active site;feature(ACTIVE SITE);String;g8;50;1000;95;false;false
+Binding site;feature(BINDING SITE);String;g8;50;1000;95;false;false
+DNA binding;feature(DNA BINDING);String;g8;50;1000;95;false;false
+Metal binding;feature(METAL BINDING);String;g8;50;1000;95;false;false
+Nucleotide binding;feature(NP BIND);String;g8;50;1000;95;false;false
+Site;feature(SITE);String;g8;50;1000;95;false;false
+Annotation;annotation score;String;g6;50;1000;95;false;true
+Features;features;String;g6;50;1000;95;false;true
+Caution;comment(CAUTION);String;g6;50;1000;95;false;false
+Miscellaneous [CC];comment(GENERAL);String;g6;50;1000;95;false;false
+Keywords;keywords;String;g6;50;1000;95;false;true
+Protein existence;existence;String;g6;50;1000;95;false;true
+Status;reviewed;String;g6;50;1000;95;false;true
+ALL;entry name;String;g7;50;1000;95;false;true;
+Subunit structure [CC];comment(SUBUNIT);String;g9;50;1000;95;false;false
+Interacts with;interactor;String;g9;50;1000;95;false;true
+Developmental stage;comment(DEVELOPMENTAL STAGE);String;g10;50;1000;95;false;false
+Induction;comment(INDUCTION);String;g10;50;1000;95;false;false
+Tissue specificity;comment(TISSUE SPECIFICITY);String;g10;50;1000;95;false;false
+Gene ontology (GO);go;String;g11;50;1000;95;false;true
+Gene ontology (biological process);go(biological process);String;g11;50;1000;95;false;false
+Gene ontology (molecular function);go(molecular function);String;g11;50;1000;95;false;false
+Gene ontology (cellular component);go(cellular component);String;g11;50;1000;95;false;false
+Gene ontology IDs;go-id;String;g11;50;1000;95;false;true
+Allergenic properties;comment(ALLERGEN);String;g12;50;1000;95;false;false
+Biotechnological use;comment(BIOTECHNOLOGY);String;g12;50;1000;95;false;false
+Disruption phenotype;comment(DISRUPTION PHENOTYPE);String;g12;50;1000;95;false;false
+Involvement in disease;comment(DISEASE);String;g12;50;1000;95;false;false
+Pharmaceutical use;comment(PHARMACEUTICAL);String;g12;50;1000;95;false;false
+Toxic dose;comment(TOXIC DOSE);String;g12;50;1000;95;false;false
+Subcellular location [CC];comment(SUBCELLULAR LOCATION);String;g13;50;1000;95;false;false
+Intramembrane;feature(INTRAMEMBRANE);String;g13;50;1000;95;false;false
+Topological domain;feature(TOPOLOGICAL DOMAIN);String;g13;50;1000;95;false;false
+Transmembrane;feature(TRANSMEMBRANE);String;g13;50;1000;95;false;false
+Post-translational modification;comment(PTM);String;g14;50;1000;95;false;false
+Chain;feature(CHAIN);String;g14;50;1000;95;false;false
+Cross-link;feature(CROSS LINK);String;g14;50;1000;95;false;false
+Disulfide bond;feature(DISULFIDE BOND);String;g14;50;1000;95;false;false
+Glycosylation;feature(GLYCOSYLATION);String;g14;50;1000;95;false;false
+Initiator methionine;feature(INITIATOR METHIONINE);String;g14;50;1000;95;false;false
+Lipidation;feature(LIPIDATION);String;g14;50;1000;95;false;false
+Modified residue;feature(MODIFIED RESIDUE);String;g14;50;1000;95;false;false
+Peptide;feature(PEPTIDE);String;g14;50;1000;95;false;false
+Propeptide;feature(PROPEPTIDE);String;g14;50;1000;95;false;false
+Signal peptide;feature(SIGNAL);String;g14;50;1000;95;false;false
+Transit peptide;feature(TRANSIT);String;g14;50;1000;95;false;false
+3D;3d;String;g15;50;1000;95;false;false
+Beta strand;feature(BETA STRAND);String;g15;50;1000;95;false;false
+Helix;feature(HELIX);String;g15;50;1000;95;false;false
+Turn;feature(TURN);String;g15;50;1000;95;false;false
+PubMed ID;citation;String;g16;50;1000;95;false;true
+Date of creation;created;String;g17;80;150;100;true;true
+Date of last modification;last-modified;String;g17;80;150;100;true;true
+Date of last sequence modification;sequence-modified;String;g17;80;150;100;false;true
+Version (entry);version(entry);int;g17;80;100;80;false;false
+Domain [CC];comment(DOMAIN);String;g18;80;1000;95;false;false
+Sequence similarities;comment(SIMILARITY);String;g18;50;1000;95;false;false
+Protein families;families;String;g18;50;1000;95;false;true
+Coiled coil;feature(COILED COIL);String;g18;50;1000;95;false;false
+Compositional bias;feature(COMPOSITIONAL BIAS);String;g18;50;1000;95;false;false
+Domain [FT];feature(DOMAIN EXTENT);String;g18;50;1000;95;false;false
+Motif;feature(MOTIF);String;g18;50;1000;95;false;false
+Region;feature(REGION);String;g18;50;1000;95;false;false
+Repeat;feature(REPEAT);String;g18;50;1000;95;false;false
+Zinc finger;feature(ZINC FINGER);String;g18;50;1000;95;false;false
+Cross-reference (EMBL);database(EMBL);String;g2;50;1000;95;false;false
+Cross-reference (PDB);database(PDB);String;g2;50;1000;95;false;false
+Cross-reference (ENSEMBL);database(ENSEMBL);String;g2;50;1000;95;false;false
+Cross-reference (PFAM);database(PFAM);String;g2;50;1000;95;false;false
+Cross-reference (RFAM);database(RFAM);String;g2;50;1000;95;false;false
+Cross-reference (CATH);database(CATH);String;g2;50;1000;95;false;false
+Cross-reference (SCOPE);database(SCOPE);String;g2;50;1000;95;false;false
+Cross-reference (GO);database(GO);String;g2;50;1000;95;false;false
+Cross-reference (INTERPRO);database(INTERPRO);String;g2;50;1000;95;false;false
+Mapped PubMed ID;citationmapping;String;g16;50;1000;95;false;true
+#
index 9ae45b9..fe8e1a9 100755 (executable)
@@ -27,7 +27,7 @@
                                <tocitem text="PDB Sequence Fetcher" target="pdbfetcher" />
                                <tocitem text="PDB Structure Chooser" target="pdbchooser" />
                                <tocitem text="Chimera Viewer" target="chimera" />
-                               <tocitem text="Select columns by annotation" target="selectcolbyannot" />
+                               <tocitem text="Select Columns by Annotation" target="selectcolbyannot" />
                                <tocitem text="Latest Release Notes" target="release"/>
                </tocitem>
                
@@ -49,7 +49,7 @@
                <tocitem text="Viewing Trees" target="treeviewer" expand="false" />
                <tocitem text="Fetching Sequences" target="seqfetch" />         
                
-               <tocitem text="Select columns by annotation" target="selectcolbyannot" />
+               <tocitem text="Select Columns by Annotation" target="selectcolbyannot" />
                
                <tocitem text="Nucleic Acid Support" target="nucleicAcids" expand="false">
                        <tocitem text="Viewing RNA structure" target="varna" />
                        <tocitem text="Consensus" target="calcs.consensus" />
                        <tocitem text="RNA Structure Consensus" target="calcs.alstrconsensus" />
                        <tocitem text="Annotations File Format" target="annotations.fileformat" />
-                       <tocitem text="Select Column by Annotation" target="calcs.annotation" />
+                       <tocitem text="Select Columns by Annotation" target="selectcolbyannot" />
                </tocitem>
                <tocitem text="3D Structure Data" target="viewingpdbs" expand="false">
                        <tocitem text="PDB Sequence Fetcher" target="pdbfetcher" />
                                <tocitem text="Annotations Menu" target="alwAnnotations" />
                                <tocitem text="Format Menu" target="alwFormat" />
                                <tocitem text="Colour Menu" target="alwColour" />
-                               <tocitem text="Calculation Menu" target="alwCalc" />
+                               <tocitem text="Calculate Menu" target="alwCalc" />
                                <tocitem text="Web Service Menu" target="wsMenu" />
                                <tocitem text="Annotation Panel Menu" target="annotPanelMenu" />
                                <tocitem text="Popup Menu" target="popMenu" />
index 545f0c1..744370b 100755 (executable)
     followed by a <em>description</em> for the row, which is shown in a
     tooltip when the user mouses over the annotation row's label. Since
     Jalview 2.7, the description field may also contain HTML tags (in
-    the same way as a <a href="featuresFile.html">sequence feature's</a>
+    the same way as a <a href="featuresFormat.html">sequence feature's</a>
     label), providing the text is enclosed in an &lt;html/&gt; tag.
   <ul>
     <em>Please note: URL links embedded in HTML descriptions are
index 9164afd..200fc8f 100755 (executable)
     the bottom of the list is rendered <em>below</em> a feature higher
     up in the list.<br> <em><strong>You can change
         the order of a feature by dragging it up and down the list with
-        the mouse</strong></em>.
+        the mouse (not applet)</strong></em>.
   </p>
   <p>
     The <strong><em>Optimise order</em></strong> button (currently only
index adabdf1..9aa341b 100644 (file)
@@ -27,7 +27,7 @@
     <strong>The Groovy Shell</strong>
   </p>
   <p>
-    <a href="http://groovy.codehaus.org/">Groovy</a> is an &quot;<em>agile
+    <a href="http://www.groovy-lang.org/">Groovy</a> is an &quot;<em>agile
       and dynamic language for the Java platform</em>&quot;. The groovy
     scripting language makes it extremely easy to programmatically
     interact with Java programs, in much the same way that Javascript is
     page.
   </p>
   <p>
-    <strong><em>Getting Groovy...</em> </strong><br> Jalview Groovy
-    support is only possible if the core groovy jars which include the
-    GroovyShell are present on the CLASSPATH when Jalview is started.
-  </p>
-  <p>
-    The jars are obtained from the <em>embedded</em> directory within
-    the <a href="http://dist.codehaus.org/groovy/distributions">groovy
-      distribution</a>. The easiest way of adding them to the Jalview
-    classpath is to download and build Jalview from its source
-    distribution, and then add the groovy-all-*.jar to the lib directory
-    whose path is given in the java.ext.dirs property.
-  </p>
-  <p>
-    <strong>Opening Jalview's Groovy Console</strong><br>If groovy
-    is available, then the <strong>Tools&#8594;Groovy
-      Console...</strong> menu entry will be available from the Jalview Desktop's
-    drop-down menu. Selecting this will open the <a
-      href="http://groovy.codehaus.org/Groovy+Console"
-    >Groovy Console</a> which allows you to interactively execute Groovy
+    <strong><em>Getting Groovy...</em> </strong><br> Jalview comes with
+    an embedded installation of Groovy. All you need is to select <strong>Tools&#8594;Groovy
+      Console...</strong> menu option from the Jalview Desktop's
+    drop-down menu. After a short pause, you should then see the <a
+      href="http://groovy-lang.org/groovyconsole.html"
+    >Groovy Console</a> appear. This allows you to interactively execute Groovy
     scripts within the Jalview run-time environment.
   </p>
   <p>
diff --git a/help/html/features/mmcif.html b/help/html/features/mmcif.html
new file mode 100644 (file)
index 0000000..6df0fd4
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>mmCIF File Format</title>
+</head>
+<body>
+       <strong>mmCIF File Format</strong>
+       <p>The mmCIF file format (macromolecular Crystallographic
+               Information) was developed under the auspices of the International Union of Crystallography (IUCr) to extend the Crystallographic Information
+               File (CIF) data representation used for describing small molecule
+               structures and associated diffraction experiments.</p>
+       <strong>Merits of mmCIF file format</strong>
+       <ul>
+               <li>Large structures (containing >62 chains and/or 99999 ATOM
+                       records) that cannot be fully represented in the PDB file format are
+                       available in the PDB archive as single PDBx/mmCIF files.</li>
+               <li>PDBx/mmCIF file format provides richer data annotation</li>         
+               <li>PDBx/mmCIF became the standard PDB archive format in 2014.
+                       Since 2016 the PDB File Format is no longer being modified or
+                       extended to support new content.
+               </li>
+       </ul>
+
+       <em>mmCIF file format support for importing 3D structure data from
+               flat file and EMBL-PDBe via mmCIF was added in Jalview 2.9.1</em>
+</body>
+</html>
\ No newline at end of file
index ac63c9e..60ac6ab 100644 (file)
@@ -42,5 +42,7 @@
     retrieved by the <a href="seqfetch.html">Sequence Fetcher</a>, and
     allows sequence features to be mapped directly from Uniprot das
     sources to their coding region on EMBL sequence records.
+  </p>
+   <p>In Jalview 2.9.1 <a href="siftsmapping.html">SIFTS Mapping</a> was added as a better means for explicitly identifying the coordinates corresponding to a displayed sequence when viewing a PDB structure associated with a sequence </p>
 </body>
 </html>
diff --git a/help/html/features/sifts_mapping_output.png b/help/html/features/sifts_mapping_output.png
new file mode 100644 (file)
index 0000000..3c28b81
Binary files /dev/null and b/help/html/features/sifts_mapping_output.png differ
diff --git a/help/html/features/siftsmapping.html b/help/html/features/siftsmapping.html
new file mode 100644 (file)
index 0000000..c344a20
--- /dev/null
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>SIFTS Mapping</title>
+</head>
+<body>
+  <p><strong>SIFTS Mapping</strong></p>
+  
+  <p>
+       SIFTS (Structure integration with function, taxonomy
+       and sequences) provides an up-to-date resource for residue-level
+       mapping between Uniprot and PDB entries. The information is updated and
+       released weekly simultaneously with the release of new PDB entries.
+       SIFTS Entries are published as XML files and made publicly available via an FTP
+       site hosted at the European Bioinformatics Institute. 
+  </p>
+       
+  <p>
+    At the point of viewing a PDB structure, Jalview downloads a SIFTS file 
+       for the target entry and uses it to accurately map the sequence residues with the 
+       structure residue. Prior to SIFTS integration, Jalview uses Needleman and Wunsch 
+       Alignment algorithm to  map sequence residues to structure residues, and that may not 
+       always result to a correct mapping since it is computational determined.        
+  </p>
+  
+  <p>
+       The default method for 'Sequence &harr; Structure' mapping can be configured 
+       in the Structure tab in the <strong>Tools &rarr; Preferences</strong> dialog box. When 'SIFTS' 
+       is enabled as the default, all mappings between 'Sequence &harr; Structure' is 
+       performed via SIFTS provided that there is a valid SIFTS entry for PDB structure. If no 
+       valid SIFTS resource is available, then the 'Sequence &harr; Structure' mapping falls 
+       back to Needleman and Wunsch Alignment algorithm.
+  </p>
+       
+  <p>To verify the mapping method used, you can view the mapping output via the structure viewer menu <strong>File &rarr; View mapping.</strong> A sample mapping output can be seen in the screenshot below. The highlighted position shows the method used. </p>     
+  <p>
+       <img src="sifts_mapping_output.png" align="left" alt="SIFTS mapping output" />
+  </p>
+       
+  <p><em>SIFTS Mapping integration was added in Jalview 2.9.1</em></p>
+       
+</body>
+</html>
\ No newline at end of file
index ed5bef3..1c36abd 100644 (file)
@@ -53,7 +53,7 @@
     <li>On selecting rows, columns or regions in one alignment, the
       corresponding selection is made in the other</li>
     <li>Sequence ordering in one alignment (using the cursor, or <strong><a
-        href="../calculate/sorting.html"
+        href="../calculations/sorting.html"
       >"Calculate&#8594;Sort")</a></strong> is also applied to the other
     </li>
     <li>Editing (gap insertion / deletion) in the protein alignment
@@ -75,7 +75,7 @@
       panels.</li>
     <li>Panel heights are adjusted dragging the divider between
       them using the mouse</li>
-    <li><a href="menus/alwview.html"><strong>"View&#8594;New
+    <li><a href="../menus/alwview.html"><strong>"View&#8594;New
           View / Expand Views / Gather Views"</strong></a> behave as for a normal
       alignment window, but always create new views as Split Frames</li>
   </ul>
diff --git a/help/html/features/uniprotqueryfields.html b/help/html/features/uniprotqueryfields.html
new file mode 100644 (file)
index 0000000..376180a
--- /dev/null
@@ -0,0 +1,599 @@
+<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>UniProtKB query fields</title>
+</head>
+
+<body>
+  <p>
+    <strong>UniProtKB query fields</strong>
+  </p>
+<p>Supported query fields for searching specific data in UniProtKB (see also <a href="text-search">query syntax</a>).</p>
+
+<table  border="1" width="95%">
+  <tr>
+    <th>Field</th>
+    <th>Example</th>
+    <th>Description</th>
+  </tr>
+  <tr>
+    <td>accession</td>
+    <td>
+      <code>accession:P62988</code>
+    </td>
+    <td>
+        Lists all entries with the primary or secondary
+        accession number P62988.
+    </td>
+  </tr>
+  <tr>
+    <td>active</td>
+    <td>
+      <code>active:no </code>
+    </td>
+    <td>
+        Lists all obsolete entries.
+    </td>
+  </tr>
+  <tr>
+    <td>annotation</td>
+    <td>
+      <code>
+        annotation:(type:non-positional)
+        <br />
+        annotation:(type:positional)
+        <br />
+        annotation:(type:mod_res "Pyrrolidone carboxylic acid" evidence:experimental)
+      </code>
+    </td>
+    <td>
+      Lists all entries with:
+      <ul>
+        <li>any general annotation (comments [CC])</li>
+        <li>any sequence annotation (features [FT])</li>
+        <li>at least one amino acid modified with a Pyrrolidone carboxylic acid group</li>
+      </ul>
+    </td>
+  </tr>
+  <tr>
+    <td>author</td>
+    <td>
+      <code>
+        author:ashburner
+      </code>
+    </td>
+    <td>
+        Lists all entries with at least one reference co-authored by Michael Ashburner.
+    </td>
+  </tr>
+  <tr>
+    <td>cdantigen</td>
+    <td>
+      <code>
+        cdantigen:CD233
+      </code>
+    </td>
+    <td>
+        Lists all entries whose cluster of differentiation number is CD233.
+    </td>
+  </tr>
+  <tr>
+    <td>citation</td>
+    <td>
+      <code>
+        citation:("intracellular structural proteins")
+        <br />
+        citation:(author:ashburner journal:nature)
+        citation:9169874
+      </code>
+    </td>
+    <td>
+      Lists all entries with a literature citation:
+      <ul>
+        <li>containing the phrase "intracellular structural proteins" in either title or abstract</li>
+        <li>co-authored by Michael Ashburner and published in Nature</li>
+        <li>with the PubMed identifier 9169874</li>
+      </ul>
+    </td>
+  </tr>
+  <tr>
+    <td>cluster</td>
+    <td>
+      <code>
+        cluster:UniRef90_A5YMT3
+      </code>
+    </td>
+    <td>
+        Lists all entries in the UniRef 90% identity cluster whose
+        representative sequence is UniProtKB entry A5YMT3.
+    </td>
+  </tr>
+  <tr>
+       <td>count</td>
+       <td>
+               <code>
+                       annotation:(type:transmem count:5)<br />
+                       annotation:(type:transmem count:[5 TO *])<br />
+                       annotation:(type:cofactor count:[3 TO *])
+               </code>
+       </td>
+       <td>Lists all entries with:
+               <ul>
+                       <li>exactly 5 transmembrane regions</li>
+                       <li>5 or more transmembrane regions</li>
+                       <li>3 or more Cofactor comments</li>
+               </ul>
+       </td>
+  </tr>
+  <tr>
+    <td>created</td>
+    <td>
+      <code>
+        created:[20121001 TO *]<br />
+        reviewed:yes AND created:[current TO *]
+      </code>
+    </td>
+    <td>
+        Lists all entries created since October 1st 2012.<br />
+        Lists all new UniProtKB/Swiss-Prot entries in the last release.
+    </td>
+  </tr>
+  <tr>
+    <td>database</td>
+    <td>
+      <code>
+        database:(type:pfam)
+        <br />
+        database:(type:pdb 1aut)
+      </code>
+    </td>
+    <td>
+      Lists all entries with:
+      <ul>
+        <li>a cross-reference to the Pfam database</li>
+        <li>a cross-reference to the PDB database entry 1aut</li>
+      </ul>
+     
+    </td>
+  </tr>
+  <tr>
+    <td>domain</td>
+    <td>
+      <code>
+        domain:VWFA
+      </code>
+    </td>
+    <td>
+        Lists all entries with a Von Willebrand factor type A domain described
+        in the 'Family and Domains' section.
+    </td>
+  </tr>
+  <tr>
+    <td>ec</td>
+    <td>
+      <code>
+        ec:3.2.1.23
+      </code>
+    </td>
+    <td>
+        Lists all beta-galactosidases.
+    </td>
+  </tr>
+  <tr>
+       <td>evidence</td>
+       <td>
+               <code>
+                       annotation:(type:signal evidence:ECO_0000269)<br />
+                       (type:mod_res phosphoserine evidence:ECO_0000269)<br />
+                       annotation:(type:function AND evidence:ECO_0000255)
+               </code>
+       </td>
+       <td>Lists all entries with:
+               <ul>
+                       <li>a signal sequence whose positions have been experimentally proven</li>
+                       <li>experimentally proven phosphoserine sites</li>
+                       <li>a function manually asserted according to rules</li>
+               </ul>
+       </td>
+  </tr>
+  <tr>
+    <td>family</td>
+    <td>
+      <code>
+        family:serpin
+      </code>
+    </td>
+    <td>
+        Lists all entries belonging to the Serpin family of proteins.
+    </td>
+  </tr>
+  <tr>
+    <td>fragment</td>
+    <td>
+      <code>
+        fragment:yes
+      </code>
+    </td>
+    <td>
+        Lists all entries with an incomplete sequence.
+    </td>
+  </tr>
+
+  <tr>
+    <td>gene</td>
+    <td>
+      <code>
+        gene:HSPC233
+      </code>
+    </td>
+    <td>
+        Lists all entries for proteins encoded by gene HSPC233.
+    </td>
+  </tr>
+  <tr>
+    <td>go</td>
+    <td>
+      <code>
+        go:cytoskeleton
+        <br />
+        go:0015629
+      </code>
+    </td>
+    <td>
+      Lists all entries associated with:
+      <ul>
+        <li>a GO term containing the word "cytoskeleton"</li>
+        <li>the GO term Actin cytoskeleton and any subclasses</li>
+      </ul>
+    </td>
+  </tr>
+  <tr>
+    <td>host</td>
+    <td>
+      <code>
+        host:mouse
+        <br />
+        host:10090
+        <br />
+        host:40674
+      </code>
+    </td>
+    <td>
+      Lists all entries for viruses infecting:
+      <ul>
+        <li>organisms with a name containing the word "mouse"</li>
+        <li>Mus musculus (Mouse)</li>
+        <li>all mammals (all taxa classified under the taxonomy node for Mammalia)</li>
+      </ul>
+    </td>
+  </tr>
+  <tr>
+    <td>id</td>
+    <td>
+      <code>id:P00750</code>
+    </td>
+    <td>
+        Returns the entry with the primary
+        accession number P00750.
+    </td>
+  </tr>
+  <tr>
+    <td>inn</td>
+    <td>
+      <code>
+        inn:Anakinra
+      </code>
+    </td>
+    <td>
+        Lists all entries whose "International Nonproprietary Name" is Anakinra.
+    </td>
+  </tr>
+  <tr>
+    <td>interactor</td>
+    <td>
+      <code>
+        interactor:P00520
+      </code>
+    </td>
+    <td>
+        Lists all entries describing interactions with the protein described by
+        entry P00520.
+    </td>
+  </tr>
+  <tr>
+    <td>keyword</td>
+    <td>
+      <code>
+        keyword:toxin
+      </code>
+    </td>
+    <td>
+        Lists all entries associated with the keyword Toxin.
+    </td>
+  </tr>
+  <tr>
+    <td>length</td>
+    <td>
+      <code>
+        length:[500 TO 700]
+      </code>
+    </td>
+    <td>
+        Lists all entries describing sequences of length between 500 and 700 residues.
+    </td>
+  </tr>
+  <tr>
+    <td>lineage</td>
+    <td />
+    <td>
+      This field is a synonym for the field <code>taxonomy</code>.
+    </td>
+  </tr>
+  <tr>
+    <td>mass</td>
+    <td>
+      <code>
+        mass:[500000 TO *]
+      </code>
+    </td>
+    <td>
+        Lists all entries describing sequences with a mass of at least 500,000 Da.
+    </td>
+  </tr>
+  <tr>
+    <td>method</td>
+    <td>
+      <code>
+        method:maldi
+        <br />
+        method:xray
+      </code>
+    </td>
+    <td>
+        Lists all entries for proteins identified by: matrix-assisted laser
+        desorption/ionization (MALDI), crystallography (X-Ray). The
+        <code>method</code> field searches names of physico-chemical
+        identification methods in the 'Biophysicochemical properties' subsection of the 'Function' section, the 'Publications' and
+        'Cross-references' sections.
+    </td>
+  </tr>
+  <tr>
+    <td>mnemonic</td>
+    <td>
+      <code>
+        mnemonic:ATP6_HUMAN
+      </code>
+    </td>
+    <td>
+        Lists all entries with entry name (ID) ATP6_HUMAN. Searches also
+        obsolete entry names.
+    </td>
+  </tr>
+  <tr>
+    <td>modified</td>
+    <td>
+      <code>
+        modified:[20120101 TO 20120301]<br />
+        reviewed:yes AND modified:[current TO *]
+      </code>
+    </td>
+    <td>
+        Lists all entries that were last modified between January and March 2012.<br />
+        Lists all UniProtKB/Swiss-Prot entries modified in the last release.
+    </td>
+  </tr>
+  <tr>
+    <td>name</td>
+    <td>
+      <code>
+        name:"prion protein"
+      </code>
+    </td>
+    <td>
+        Lists all entries for prion proteins.
+    </td>
+  </tr>
+  <tr>
+    <td>organelle</td>
+    <td>
+      <code>
+        organelle:Mitochondrion
+      </code>
+    </td>
+    <td>
+        Lists all entries for proteins encoded by a gene of the mitochondrial
+        chromosome.
+    </td>
+  </tr>
+  <tr>
+    <td>organism</td>
+    <td>
+      <code>
+        organism:"Ovis aries"
+        <br />
+        organism:9940
+        <br />
+        organism:sheep
+        <br />
+      </code>
+    </td>
+    <td>
+        Lists all entries for proteins expressed in sheep (first 2 examples) and
+        organisms whose name contains the term "sheep".
+    </td>
+  </tr>
+  <tr>
+    <td>plasmid</td>
+    <td>
+      <code>
+        plasmid:ColE1
+      </code>
+    </td>
+    <td>
+        Lists all entries for proteins encoded by a gene of plasmid ColE1.
+    </td>
+  </tr>
+  <tr>
+    <td>proteome</td>
+    <td>
+      <code>
+        proteome:UP000005640
+      </code>
+    </td>
+    <td>
+        Lists all entries from the human proteome.
+    </td>
+  </tr>
+  <tr>
+    <td>proteomecomponent</td>
+    <td>
+      <code>
+        proteomecomponent:"chromosome 1" and organism:9606
+      </code>
+    </td>
+    <td>
+        Lists all entries from the human chromosome 1.
+    </td>
+  </tr>
+  <tr>
+    <td>replaces</td>
+    <td>
+      <code>
+        replaces:P02023
+      </code>
+    </td>
+    <td>
+        Lists all entries that were created from a merge with entry P02023.
+    </td>
+  </tr>
+  <tr>
+    <td>reviewed</td>
+    <td>
+      <code>
+        reviewed:yes
+      </code>
+    </td>
+    <td>
+        Lists all UniProtKB/Swiss-Prot entries.
+    </td>
+  </tr>
+  <tr>
+    <td>scope</td>
+    <td>
+      <code>
+        scope:mutagenesis
+      </code>
+    </td>
+    <td>
+        Lists all entries containing a reference that was used to gather
+        information about mutagenesis.
+    </td>
+  </tr>
+  <tr>
+    <td>sequence</td>
+    <td>
+      <code>
+        sequence:P05067-9
+      </code>
+    </td>
+    <td>
+        Lists all entries containing a link to isoform 9 of the sequence
+        described in entry P05067. Allows searching by specific sequence
+        identifier.
+    </td>
+  </tr>
+  <tr>
+    <td>sequence_modified</td>
+    <td>
+      <code>
+        sequence_modified:[20120101 TO 20120301]<br />
+        reviewed:yes AND sequence_modified:[current TO *]
+      </code>
+    </td>
+    <td>
+        Lists all entries whose sequences were last modified between January and March 2012.<br />
+        Lists all UniProtKB/Swiss-Prot entries whose sequences were modified in the last release.
+    </td>
+  </tr>
+  <tr>
+    <td>source</td>
+    <td>
+      <code>
+        source:intact
+      </code>
+    </td>
+    <td>
+        Lists all entries containing a GO term whose annotation source is the
+        IntAct database.
+    </td>
+  </tr>
+  <tr>
+    <td>strain</td>
+    <td>
+      <code>
+        strain:wistar
+      </code>
+    </td>
+    <td>
+        Lists all entries containing a reference relevant to strain wistar.
+    </td>
+  </tr>
+  <tr>
+    <td>taxonomy</td>
+    <td>
+      <code>
+        taxonomy:40674
+      </code>
+    </td>
+    <td>
+        Lists all entries for proteins expressed in Mammals. This field is used to retrieve
+        entries for all organisms classified below a given taxonomic node taxonomy classification).
+    </td>
+  </tr>
+  <tr>
+    <td>tissue</td>
+    <td>
+      <code>
+        tissue:liver
+      </code>
+    </td>
+    <td>
+        Lists all entries containing a reference describing the protein sequence
+        obtained from a clone isolated from liver.
+    </td>
+  </tr>
+  <tr>
+    <td>web</td>
+    <td>
+      <code>
+        web:wikipedia
+      </code>
+    </td>
+    <td>
+        Lists all entries for proteins that are described in Wikipedia.
+    </td>
+  </tr>
+</table>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/help/html/features/uniprotseqfetcher.png b/help/html/features/uniprotseqfetcher.png
new file mode 100644 (file)
index 0000000..a592e8e
Binary files /dev/null and b/help/html/features/uniprotseqfetcher.png differ
diff --git a/help/html/features/uniprotsequencefetcher.html b/help/html/features/uniprotsequencefetcher.html
new file mode 100644 (file)
index 0000000..55b4d71
--- /dev/null
@@ -0,0 +1,161 @@
+<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>The Uniprot Free Text Search Interface</title>
+</head>
+<body>
+
+  <strong>The Uniprot Free Text Search Interface</strong>
+  <p>
+    Jalview provides a specialised interface that allows fast and
+    efficient discovery and retrieval of data from the Uniprot database.
+    It allows
+    interactive querying of Uniprot metadata with free text and structured
+    queries, so sequences can be located without prior knowledge of
+    their database accessions, or <em>via</em> manual cross-referencing
+    from Uniprot or other bioinformatics websites.
+  </p>
+  <p>
+    To open the UniProt Sequence Fetcher, select UniProt as the database from
+    any <a href="seqfetch.html">Sequence Fetcher</a> dialog (opened <em>via</em>
+    <strong>&quot;File &#8594;Fetch Sequences&quot;</strong>).
+  </p>
+  <p>
+  <img src="uniprotseqfetcher.png" align="left"
+    alt="Uniprot sequence fetcher (introduced in Jalview 2.9.1)"
+  />
+  </p>
+
+  <p>
+    <strong>Searching the Uniprot Database</strong>
+  </p>
+  <p>
+    To search the Uniprot, begin typing in the text box. The results of your
+    query are shown in the search results tab, which queries Uniprot after 1.5secs every time
+    you type in the search text box. You can sort results according to
+    the displayed columns, and select entries with the mouse or
+    keyboard. Once you have selected one or more entries, hit the <strong>OK</strong>
+    button to retrieve and visualise the sequences in Jalview Alignment interface.
+  </p>
+  <ul>
+    <li><strong>Searching a specific Uniprot field </strong> If you
+      want to find sequences based on a specific Uniprot metadata field,
+      you can select it from the drop-down menu.</li>
+      
+
+               <li><strong>Bulk Uniprot retrieval</strong><br>
+      Firstly, switch the search target to Uniprot Id, then enter multiple IDs by separating them with a semi-colon.
+      e.g. fila_human; mnt_human; mnt_mouse<br />Hitting Return or OK will automatically
+      fetch those IDs, like the default Sequence Fetcher interface.</li>
+      
+            <li><strong>Advanced / Custom querying</strong>  
+      The table below provides a brief overview of the supported Uniprot query syntax (see <a href="uniprotqueryfields.html">query fields for UniProtKB</a>):
+               <table border="1" width="95%">
+                               <tr>
+                                       <td><code>human antigen</code></td>
+                                       <td rowspan="3">All entries containing both terms.</td>
+                               </tr>
+                               <tr>
+                                       <td><code>human AND antigen</code></td>
+                               </tr>
+                               <tr>
+                                       <td><code>human &amp;&amp; antigen</code></td>
+                               </tr>
+                               <tr>
+                                       <td><code>"human antigen"</code></td>
+                                       <td>All entries containing both terms in the exact order.</td>
+                               </tr>
+                               <tr>
+                                       <td><code>human -antigen</code></td>
+                                       <td rowspan="3">All entries containing the term <code>human</code>
+                                               but not <code>antigen</code>.
+                                       </td>
+                               </tr>
+                               <tr>
+                                       <td><code>human NOT antigen</code></td>
+                               </tr>
+                               <tr>
+                                       <td><code>human ! antigen</code></td>
+                               </tr>
+                               <tr>
+                                       <td><code>human OR mouse</code></td>
+                                       <td rowspan="2">All entries containing either term.</td>
+                               </tr>
+                               <tr>
+                                       <td><code>human || mouse</code></td>
+                               </tr>
+                               <tr>
+                                       <td><code>antigen AND (human OR mouse)</code></td>
+                                       <td>Using parentheses to override boolean precedence rules.</td>
+                               </tr>
+                               <tr>
+                                       <td><code>anti*</code></td>
+                                       <td>All entries containing terms starting with <code>anti</code>.
+                                               Asterisks can also be used at the beginning and within terms. <strong>Note:</strong>
+                                               Terms starting with an asterisk or a single letter followed by an
+                                               asterisk can slow down queries considerably.
+                                       </td>
+                               </tr>
+                               <tr>
+                                       <td><code> author:Tiger*</code></td>
+                                       <td>Citations that have an author whose name starts with <code>Tiger</code>.
+                                               To search in a specific field of a dataset, you must prefix your
+                                               search term with the field name and a colon. To discover what
+                                               fields can be queried explicitly, observe the query hints that are
+                                               shown after submitting a query or use the query builder (see
+                                               below).
+                                       </td>
+                               </tr>
+                               <tr>
+                                       <td><code>length:[100 TO *]</code></td>
+                                       <td>All entries with a sequence of at least 100 amino acids.</td>
+                               </tr>
+                               <tr>
+                                       <td><code>citation:(author:Arai author:Chung)</code></td>
+                                       <td>All entries with a publication that was coauthored by two
+                                               specific authors.</td>
+                               </tr>
+                       </table>
+               </li>
+</ul>
+  <p>
+    <strong>Result pagination</strong>
+  </p>
+  The query results returned from the Uniprot server are paginated for performance optimisation. 
+  The button labelled <strong>' << '</strong> and <strong>' >> '</strong> can be used to navigate to the next or previous result page respectively. 
+  The page range is shown on the title bar of the Free Text Search interface. Jalview's pagination implementation supports multiple selection of entries across multiple pages. 
+  
+  
+ <p>
+    <strong>Customising The Uniprot Sequence Fetcher</strong>
+  </p>
+  <p>
+    To change the displayed meta-data in the search result, click the
+    'Customise Displayed Options' tab, and select the fields you'd like
+    to displayed or remove. 
+  </p>
+  <p>
+    <em>The Uniprot Free Test Search Interface was introduced in
+      Jalview 2.9.1</em>
+  </p>
+</body>
+</html>
\ No newline at end of file
index 4d35516..d4819f1 100755 (executable)
   </p>
 
   <p>
+    <strong>Importing PDB Entries or files in mmCIF format</strong><br>
+    <a href="mmcif.html">mmCIF file format</a> provides an alternative means for 
+    importing 3D structure data from flat file and EMBL-PDBe 
+    web-service. To enable mmCIF as the default format for 
+    importing PBD sequences from the PDB sequence fetcher, add or modify the 
+    property  
+    <code>DEFAULT_STRUCTURE_FORMAT=mmCIF</code> in Jalview properties file. 
+    Once this is done, the steps followed in retrieving PDB format files above can 
+    be followed to obtain the same data with mmCIF. <em>mmCIF format file support was added in Jalview 2.9.1.</em></p>
+    
+   
+
+  <p>
     <strong>Associating a large number of PDB files to
       sequences in an alignment</strong><br /> It is often the case when working
     with structure alignments that you will have a directory of PDB
index a477457..f129551 100755 (executable)
       <td>Both</td>
       <td>Cuts the (fully) selected sequences from the alignment. <!-- not yet in this version 
 This will not happen if only some
-columns are selected, you should use the <a href="features/regionHiding.html">Hide Regions feature</a> instead.-->
+columns are selected, you should use the <a href="features/hiddenRegions.html">Hide Regions feature</a> instead.-->
       </td>
     </tr>
     <tr>
index 2e9ea0c..34e8d75 100755 (executable)
         action on the whole alignment, or selected rows, columns, or
         regions.
     </em> <br></li>
+    <li><strong>Reverse, Reverse Complement</strong> (not applet)<br>
+    <em>These options are visible for nucleotide alignments. Selecting them adds the reverse (or reverse complement)
+    of the sequences (or selected region) as new sequences in the alignment. To try this out, add this sequence and
+    perform 'Reverse Complement' followed by 'Translate as cDNA':
+    <br><small>
+    Seq GTCATTTGCGCGTGTTGATTATTCGGACCGCTCCACTTCCCTTTACTCGTGCGTTCAATTGATTTAATCCTC
+    TGGGGGGGCTCTGGTTTACATAGCTTAAATCTATTCCATTCAAGGAAGCTCATG</small>
+    </em> <br></li>
     <li><strong>Get Cross-References</strong> (not applet)<br>
     <em>This option is visible where sequences have
         cross-references to other standard databases; for example, an
index 0d5b698..15c94ce 100755 (executable)
@@ -83,8 +83,8 @@
             dialog box.
         </em></li>
         <li><strong>Groovy Console...<em> (only
-              available if groovy is on the classpath)</em><br></strong> <em>Open's
-            the <a href="../groovy.html">Groovy Console</a> for
+              available if groovy is on the classpath)</em><br></strong> <em>Opens
+            the <a href="../features/groovy.html">Groovy Console</a> for
             interactive scripting.
         </em><strong><br></strong></li>
 
index 1eb2366..353fb41 100755 (executable)
             and sequence description to be entered. Press OK to accept
             your edit. To save sequence descriptions, you must save in
             Fasta, PIR or Jalview File format.</em></li>
-        <li><a href="sqaddrefannot"><strong>Add
-              Reference Annotations<br>
-          </strong><em>When enabled, copies any available alignment
+        <li><strong>Add <a href="../features/annotation.html#seqannots">Reference Annotations</a></strong><br>
+              <em>When enabled, copies any available alignment
               annotation for this sequence to the current view.</em></li>
         <li><strong>Set as Reference</strong> or <strong>Unmark
             as Reference</strong><br /> Sets or unsets the reference sequence for
index 23e958c..d4bbd33 100755 (executable)
     <tr>
       <td width="60" nowrap>
         <div align="center">
-          <strong><a name="Jalview.2.9.0b2">2.9.0b2</a><br />
-            <em>16/10/2015</em></strong>
+          <strong><a name="Jalview.2.9.1">2.9.1</a><br /> <em>1/6/2016</em></strong>
         </div>
       </td>
+      <td><em>General</em>
+        <ul>
+          <li></li>
+        </ul> <em>Application</em>
+        <ul>
+          <li></li>
+        </ul> <em>Applet</em>
+        <ul>
+          <li></li>
+        </ul></td>
       <td>
-      <em>General</em>
-      <ul>
-            <li>Time stamps for signed Jalview application and applet jars</li>
-      </ul>
+        <div align="left">
+          <em>General</em>
+          <ul>
+            <li></li>
+          </ul>
+          <em>Application</em>
+          <ul>
+            <li></li>
+          </ul>
+          <em>Applet</em>
+          <ul>
+            <li></li>
+          </ul>
+        </div>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" nowrap>
+        <div align="center">
+          <strong><a name="Jalview.2.9.0b2">2.9.0b2</a><br />
+            <em>16/10/2015</em></strong>
+        </div>
       </td>
+      <td><em>General</em>
+        <ul>
+          <li>Time stamps for signed Jalview application and applet
+            jars</li>
+        </ul></td>
       <td>
         <div align="left">
-        <em>Application</em><ul>
-          <li>Duplicate group consensus and conservation rows shown when tree is partitioned</li>
-            <li>Erratic behaviour when tree partitions made with multiple cDNA/Protein split views</li>
-            </ul>
+          <em>Application</em>
+          <ul>
+            <li>Duplicate group consensus and conservation rows
+              shown when tree is partitioned</li>
+            <li>Erratic behaviour when tree partitions made with
+              multiple cDNA/Protein split views</li>
+          </ul>
         </div>
       </td>
     </tr>
             <em>8/10/2015</em></strong>
         </div>
       </td>
-      <td>
-      <em>General</em>
+      <td><em>General</em>
+        <ul>
+          <li>Updated Spanish translations of localized text for
+            2.9</li>
+        </ul> <em>Application</em>
       <ul>
-            <li>Updated Spanish translations of localized text for 2.9</li>
-      </ul>
-      <em>Application</em><ul>
-      <!-- <li>cDNA/Protein splitframe window geometry preserved in Jalview projects</li>-->
-      <li>Signed OSX InstallAnywhere installer<br></li>
-      <li>Support for per-sequence based annotations in BioJSON</li></ul>
-        <em>Applet</em>
-        <ul><li>Split frame example added to applet examples page</li>
-            </ul>
-      </td>
+          <!-- <li>cDNA/Protein splitframe window geometry preserved in Jalview projects</li>-->
+          <li>Signed OSX InstallAnywhere installer<br></li>
+          <li>Support for per-sequence based annotations in BioJSON</li>
+        </ul> <em>Applet</em>
+        <ul>
+          <li>Split frame example added to applet examples page</li>
+        </ul></td>
       <td>
         <div align="left">
-        <em>General</em>
+          <em>General</em>
           <ul>
-            <li>Mapping of cDNA to protein in split frames incorrect when sequence start > 1</li>
-            <li>Broken images in filter column by annotation dialog documentation</li>
+            <li>Mapping of cDNA to protein in split frames
+              incorrect when sequence start > 1</li>
+            <li>Broken images in filter column by annotation dialog
+              documentation</li>
             <li>Feature colours not parsed from features file</li>
-            <li>Exceptions and incomplete link URLs recovered when loading a features file containing HTML tags in feature description</li>
-            
+            <li>Exceptions and incomplete link URLs recovered when
+              loading a features file containing HTML tags in feature
+              description</li>
+
           </ul>
-      <em>Application</em><ul>
-            <li>Annotations corrupted after BioJS export and reimport</li>
-            <li>Incorrect sequence limits after Fetch DB References with 'trim retrieved sequences'</li>
-            <li>Incorrect warning about deleting all data when deleting selected columns</li>
-            <li>Patch to build system for shipping properly signed JNLP templates for webstart launch</li>
-            <li>EMBL-PDBe fetcher/viewer dialogs do not offer unreleased structures for download or viewing</li>
-            <li>Tab/space/return keystroke operation of EMBL-PDBe fetcher/viewer dialogs works correctly</li>
-            <li>Disabled 'minimise' button on Jalview windows running on OSX to workaround redraw hang bug</li>
-            <li>Split cDNA/Protein view position and geometry not recovered from jalview project</li>
-            <li>Initial enabled/disabled state of annotation menu sorter 'show autocalculated first/last' corresponds to alignment view</li>
-            <li>Restoring of Clustal, RNA Helices and T-Coffee color schemes from BioJSON</li>
-            </ul>
-      <em>Applet</em><ul>
-            <li>Reorder sequences mirrored in cDNA/Protein split frame</li>
+          <em>Application</em>
+          <ul>
+            <li>Annotations corrupted after BioJS export and
+              reimport</li>
+            <li>Incorrect sequence limits after Fetch DB References
+              with 'trim retrieved sequences'</li>
+            <li>Incorrect warning about deleting all data when
+              deleting selected columns</li>
+            <li>Patch to build system for shipping properly signed
+              JNLP templates for webstart launch</li>
+            <li>EMBL-PDBe fetcher/viewer dialogs do not offer
+              unreleased structures for download or viewing</li>
+            <li>Tab/space/return keystroke operation of EMBL-PDBe
+              fetcher/viewer dialogs works correctly</li>
+            <li>Disabled 'minimise' button on Jalview windows
+              running on OSX to workaround redraw hang bug</li>
+            <li>Split cDNA/Protein view position and geometry not
+              recovered from jalview project</li>
+            <li>Initial enabled/disabled state of annotation menu
+              sorter 'show autocalculated first/last' corresponds to
+              alignment view</li>
+            <li>Restoring of Clustal, RNA Helices and T-Coffee
+              color schemes from BioJSON</li>
+          </ul>
+          <em>Applet</em>
+          <ul>
+            <li>Reorder sequences mirrored in cDNA/Protein split
+              frame</li>
             <li>Applet with Jmol examples not loading correctly</li>
-            </ul>
+          </ul>
         </div>
       </td>
     </tr>
     <tr>
       <td><div align="center">
           <strong><a name="Jalview.2.8.0b1">2.8.0b1</a><br />
-          <em>30/1/2014</em></strong>
+            <em>30/1/2014</em></strong>
         </div></td>
       <td>
         <ul>
         <ul>
           <li>URL links generated from description line for
             regular-expression based URL links (applet and application)
+
           
           <li>Non-positional feature URL links are shown in link
             menu</li>
             between different screens.</li>
           <li>New preference items for sequence ID tooltip and
             consensus annotation</li>
-          <li>Client to submit sequences and IDs to <a
-            href="webServices/index.html#envision2">Envision2</a>
-            Workflows
-          </li>
+          <li>Client to submit sequences and IDs to Envision2 Workflows</li>
           <li><em>Vamsas Capabilities</em>
             <ul>
               <li>Improved VAMSAS synchronization (Jalview archive
           <li>Cancel button for DAS Feature Fetching
           <li>PCA and PDB Viewers zoom via mouse roller
           <li>User-defined sub-tree colours and sub-tree selection
+
           
           <li>'New Window' button on the 'Output to Text box'
         </ul>
           <li>Fixed Remove Empty Columns Bug (empty columns at end
             of alignment)
           <li>Slowed DAS Feature Fetching for increased robustness.
+
           
           <li>Made angle brackets in ASCII feature descriptions
             display correctly
           <li>WsDbFetch query/result association resolved
           <li>Tree leaf to sequence mapping improved
           <li>Smooth fonts switch moved to FontChooser dialog box.
+
           
         </ul>
       </td>
index 5dd4472..6d4a461 100644 (file)
@@ -32,7 +32,7 @@
       Function, and Genetics</em> 43(2): 227-241. <a
       href="http://www.ncbi.nlm.nih.gov/pubmed/12112692"
     >PubMed</a> or available on the <a
-      href="http://www.well.ox.ac.uk/~valdar/publications.html"
+      href="http://valdarlab.unc.edu/publications.html"
     >Valdar Group publications page</a>), but the SMERFs score was
     developed later and described by Manning et al. in 2008 (<a
       href="http://www.biomedcentral.com/1471-2105/9/51"
index 04ccd8f..b03bb9d 100755 (executable)
@@ -88,7 +88,7 @@
     >REST</a> web services exposing sequence alignment, analysis, and
     secondary structure prediction programs. Originally, Jalview 2's
     services were maintained by the Barton group at the University of
-    Dundee, and ran programs on the Life Sciences High-performace
+    Dundee, and ran programs on the Life Sciences High-performance
     Computing Cluster. With the advent of <a
       href="http://www.compbio.dundee.ac.uk/JABAWS"
     >JABAWS</a>, however, it is possible for anyone to host Jalview web
index b685576..6266036 100644 (file)
       from the input</li>
     <li><em>realignment</em> - where any aligned sequences will be
       used by the service to construct a profile based alignment of the
-      remaining unaligned sequences.</li>
+      remaining unaligned sequences</li>
   </ul>
   <strong>JABAWS Alignment services</strong>
   <br> Most alignment services are provided by the
   <a href="JABAWS.html">JABAWS framework</a>, which allows you to
   customise the precise parameters used when running each alignment
-  prgoram. In addition to the 'Default settings', you may choose from a
+  program. In addition to the 'Default settings', you may choose from a
   range of alignment preset settings, or create your own using the
   <a href="webServicesParams.html">'Edit Settings And Run ..' dialog
     box</a>.
@@ -58,7 +58,7 @@
   <ul>
     <li><a href="http://www.clustal.org/">Clustal Omega and
         Clustal W</a> (version 2.0.12)</li>
-    <li><a href="http://align.bmr.kyushu-u.ac.jp/mafft/software/">Mafft</a>
+    <li><a href="http://mafft.cbrc.jp/alignment/software/">Mafft</a>
       (version 6.8.57b)</li>
     <li><a href="http://www.drive5.com/muscle">Muscle</a> (version
       3.8.31)</li>
index de0f8cd..7a23d58 100644 (file)
@@ -83,7 +83,7 @@
     <em>Please Note:
       <ul>
         <li>The regular expressions supported by Jalview are those
-          provided by the <a href="www.javaregex.com">Stevesoft
+          provided by the <a href="http://www.javaregex.com">Stevesoft
             javaregex package</a>.
         </li>
         <li>Some characters must be escaped when specifying them as
index 1cfc700..5070435 100644 (file)
Binary files a/lib/castor-1.1-cycle-xml.jar and b/lib/castor-1.1-cycle-xml.jar differ
diff --git a/lib/groovy-all-1.8.2.jar b/lib/groovy-all-1.8.2.jar
deleted file mode 100644 (file)
index 85af249..0000000
Binary files a/lib/groovy-all-1.8.2.jar and /dev/null differ
diff --git a/lib/groovy-all-2.4.6-indy.jar b/lib/groovy-all-2.4.6-indy.jar
new file mode 100644 (file)
index 0000000..5f3d51c
Binary files /dev/null and b/lib/groovy-all-2.4.6-indy.jar differ
index 7ad030a..ea5a1f4 100644 (file)
Binary files a/lib/min-jabaws-client-2.1.0.jar and b/lib/min-jabaws-client-2.1.0.jar differ
index 429fe77..182e0da 100644 (file)
Binary files a/lib/quaqua-filechooser-only-8.0.jar and b/lib/quaqua-filechooser-only-8.0.jar differ
old mode 100755 (executable)
new mode 100644 (file)
diff --git a/resources/fts/pdb_data_columns.txt b/resources/fts/pdb_data_columns.txt
new file mode 100644 (file)
index 0000000..1827a85
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+pdb_data_columns
+#
+_group.id
+_group.name
+_group.sort_order
+g1;Quality Measures;1
+g2;Cross References;2
+g3;Names & Taxonomy;3
+g4;Procedures & Software;4
+g5;Date Of;5
+g6;Miscellenous;6
+#
+_data_column.primary_key;pdb_id
+_data_column.default_response_page_size;100
+#
+_data_column.name
+_data_column.code
+_data_column.group_id
+_data_column.data_type | _data_column.isFormated | _data_column.significantDigit
+_data_column.min_col_width
+_data_column.max_col_width
+_data_column.preferred_col_width
+_data_column.is_shown_by_default
+_data_column.is_searchable
+PDB Id;pdb_id;String;g2;40;60;45;true;true
+Title;title;String;g6;300;1500;400;true;false
+Molecule;molecule_name;String;g3;50;400;95;false;true
+Molecule Type;molecule_type;String;g3;50;400;95;false;true
+Sequence;molecule_sequence;String;g6;50;400;95;false;false
+PFAM Accession;pfam_accession;String;g2;50;400;95;false;true
+PFAM Name;pfam_name;String;g3;50;400;95;false;true
+InterPro Name;interpro_name;String;g3;50;400;95;false;false
+InterPro Accession;interpro_accession;String;g2;50;400;95;false;false
+UniProt Id;uniprot_id;String;g2;50;400;95;false;true
+UniProt Accession;uniprot_accession;String;g2;50;400;95;false;false
+UniProt Coverage;uniprot_coverage;String;g6;50;400;95;false;false
+Uniprot Features;uniprot_features;String;g6;50;400;95;false;false
+R Factor;r_factor;Double|T|3;g1;50;150;85;false;false
+Experimental Method;experimental_method;String;g4;50;400;105;true;false
+Resolution;resolution;Double|T|3;g1;50;150;85;true;false
+Data Quality;data_quality;Double|T|2;g1;50;150;85;false;false
+Overall Quality;overall_quality;Double|T|1;g1;50;150;85;false;false
+Number of Polymers;number_of_polymers;int;g6;50;400;95;false;false
+Number of Protein Chains;number_of_protein_chains;int;g6;50;400;95;false;false
+Number of Bound Molecule;number_of_bound_molecules;int;g6;50;400;95;false;false
+Number of Polymer Residue;number_of_polymer_residues;int;g6;50;400;95;false;false
+GENUS;genus;String;g3;50;400;95;false;true
+Gene Name;gene_name;String;g3;50;400;95;false;true
+GO Id;go_id;String;g2;50;400;95;false;false
+Assembly Id;assembly_id;String;g2;50;400;95;false;false
+Assembly Form;assembly_form;String;g6;50;400;95;false;false
+Assembly Type;assembly_type;String;g6;50;400;95;false;false
+Space Group;spacegroup;String;g6;50;400;95;false;false
+Cath Code;cath_code;String;g2;50;400;95;false;false
+Tax Id;tax_id;String;g2;50;400;95;false;false
+Tax Query;tax_query;String;g2;50;400;95;false;false
+Interacting Entity Id;interacting_entity_id;String;g2;50;400;95;false;false
+Interacting Molecules;interacting_molecules;String;g6;50;400;95;false;false
+Pubmed Id;pubmed_id;int;g2;50;400;95;false;false
+Status;status;String;g6;50;400;95;false;false
+Model Quality;model_quality;Double|T|2;g1;50;150;85;false;false
+Pivot Resolution;pivot_resolution;Double|T|3;g1;50;150;85;false;false
+Data reduction software;data_reduction_software;String;g4;50;400;95;false;false
+Max observed residues;max_observed_residues;String;g6;50;400;95;false;false
+Organism scientific name;organism_scientific_name;String;g3;50;400;95;false;false
+Super kingdom;superkingdom;String;g3;50;400;95;false;false
+Rank;rank;String;g3;50;400;95;false;false
+Crystallisation Ph;crystallisation_ph;String;g6;50;400;95;false;false
+Biological Function;biological_function;String;g6;50;400;95;false;false
+Biological Process;biological_process;String;g6;50;400;95;false;false
+Biological Cell Component;biological_cell_component;String;g6;50;400;95;false;false
+Compound Name;compound_name;String;g3;50;400;95;false;false
+Compound Id;compound_id;String;g2;50;400;95;false;false
+Compound Weight;compound_weight;String;g6;50;400;95;false;false
+Compound Systematic Name;compound_systematic_name;String;g3;50;400;95;false;false
+Interacting Ligands;interacting_ligands;String;g6;50;400;95;false;false
+Journal;journal;String;g6;50;400;95;false;false
+All Authors;all_authors;String;g6;50;400;95;false;false
+Experiment Data Available;experiment_data_available;String;g6;50;400;95;false;false
+Diffraction Protocol;diffraction_protocol;String;g4;50;400;95;false;false
+Refinement Software;refinement_software;String;g4;50;400;95;false;false
+Structure Determination Method;structure_determination_method;String;g4;50;400;95;false;false
+Synchrotron Site;synchrotron_site;String;g6;50;400;95;false;false
+Sample Preparation Method;sample_preparation_method;String;g4;50;400;95;false;false
+Entry Authors;entry_authors;String;g6;50;400;95;false;false
+Citation Title;citation_title;String;g6;50;400;95;false;false
+Structure Solution Software;structure_solution_software;String;g4;50;400;95;false;false
+Entry Entity;entry_entity;String;g6;50;400;95;false;false
+R Free;r_free;Double|T|3;g1;50;150;85;false;false
+Number of Polymer Entities;number_of_polymer_entities;int;g6;50;400;95;false;false
+Number of Bound Entities;number_of_bound_entities;int;g6;50;400;95;false;false
+Crystallisation Reservoir;crystallisation_reservoir;String;g6;50;400;95;false;false
+Data Scalling Software;data_scaling_software;String;g4;50;400;95;false;false
+Detector;detector;String;g6;50;400;95;false;false
+Detector Type;detector_type;String;g6;50;400;95;false;false
+Modified Residue Flag;modified_residue_flag;String;g6;50;400;95;false;false
+Number of Copies;number_of_copies;int;g6;50;400;95;false;false
+Struc Asym Id;struct_asym_id;String;g2;50;400;95;false;false
+Homologus PDB Entity Id;homologus_pdb_entity_id;String;g2;50;400;95;false;false
+Molecule Synonym;molecule_synonym;String;g6;50;400;95;false;false
+Deposition Site;deposition_site;String;g6;50;400;95;false;false
+Synchrotron Beamline;synchrotron_beamline;String;g6;50;400;95;false;false
+Entity Id; entity_id;String;g2;50;400;95;false;false
+Beam Source Name;beam_source_name;String;g3;50;400;95;false;false
+Processing Site;processing_site;String;g6;50;400;95;false;false
+Entity Weight;entity_weight;Double|T|0;g6;50;400;95;false;false
+Version;_version_;Double|F|0;g6;50;400;95;false;false
+ALL;text;String;g6;50;400;95;false;true
+#
diff --git a/resources/fts/uniprot_data_columns.txt b/resources/fts/uniprot_data_columns.txt
new file mode 100644 (file)
index 0000000..723e86d
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+
+uniprot_data_columns
+#
+_group.id
+_group.name
+_group.sort_order
+g1;Quality Measures;3
+g2;Cross References;2
+g3;Names & Taxonomy;1
+g4;Procedures & Softwares;4
+g5;Date Of;5
+g6;Miscellenous;6
+g7;Sequences;7
+g8;Function;8
+g9;Interaction;9
+g10;Expression;10
+g11;Gene Ontology;11
+g12;Pathology & Biotech;12
+g13;Subcellular location;13
+g14;PTM / Processing;14
+g15;Structure;15
+g16;Publications;16
+g17;Date of;17
+g18;Family & Domain;18
+#
+_data_column.primary_key;id
+_data_column.default_response_page_size;500
+#
+_data_column.name
+_data_column.code|_data_column.alt_code (optional: used for specifying search code when different from original code)
+_data_column.group_id
+_data_column.data_type
+_data_column.min_col_width
+_data_column.max_col_width
+_data_column.preferred_col_width
+_data_column.is_shown_by_default
+_data_column.is_searchable
+Uniprot Id;id;String;g3;80;150;85;true;true
+Entry Name;entry name|mnemonic;String;g3;100;150;105;true;true
+Protein names;protein names|name;String;g3;300;1500;500;true;true
+Gene Names;genes|gene;String;g3;100;1000;145;true;true
+Organism;organism;String;g3;100;1000;200;true;true
+Organism ID;organism-id;int;g3;60;100;80;false;false
+Proteomes;proteome;String;g3;50;1000;95;false;false
+Taxonomic lineage (ALL);lineage(ALL)|taxonomy;String;g3;50;400;95;false;false
+Virus hosts;virus hosts|host;String;g3;50;1000;95;false;true
+Fragment;fragment;String;g7;50;1000;95;false;false
+Gene encoded by;encodedon;String;g7;50;1000;95;false;false
+Alternative products (isoforms);comment(ALTERNATIVE PRODUCTS);String;g7;50;1000;95;false;false
+Erroneous gene model prediction;comment(ERRONEOUS GENE MODEL PREDICTION);String;g7;50;1000;95;false;false
+Erroneous initiation;comment(ERRONEOUS INITIATION);String;g7;50;1000;95;false;false
+Erroneous translation;comment(ERRONEOUS TRANSLATION);String;g7;50;1000;95;false;false
+Frameshift;comment(FRAMESHIFT);String;g7;50;1000;95;false;false
+Mass spectrometry;comment(MASS SPECTROMETRY);String;g7;50;1000;95;false;false
+Polymorphism;comment(POLYMORPHISM);String;g7;50;1000;95;false;false
+RNA editing;comment(RNA EDITING);String;g7;50;1000;95;false;false
+Sequence caution;comment(SEQUENCE CAUTION);String;g7;50;1000;95;false;false
+Status;reviewed;String;g6;50;100;95;true;true
+Length;length;int|T|0;g7;50;100;65;true;true
+Mass;mass;String;g7;50;100;80;false;true
+Sequence;sequence;String;g7;50;1000;95;false;false
+Alternative sequence;feature(ALTERNATIVE SEQUENCE);String;g7;50;1000;95;false;false
+Natural variant;feature(NATURAL VARIANT);String;g7;50;1000;95;false;false
+Non-adjacent residues;feature(NON ADJACENT RESIDUES);String;g7;50;1000;95;false;false
+Non-standard residue;feature(NON STANDARD RESIDUE);String;g7;50;1000;95;false;false
+Non-terminal residue;feature(NON TERMINAL RESIDUE);String;g7;50;1000;95;false;false
+Sequence conflict;feature(SEQUENCE CONFLICT);String;g7;50;1000;95;false;false
+Sequence uncertainty;feature(SEQUENCE UNCERTAINTY);String;g7;50;1000;95;false;false
+Version (Sequence);version(sequence);String;g7;50;1000;95;false;false
+EC number;ec;String;g8;50;1000;95;false;true
+Absorption;comment(ABSORPTION);String;g8;50;1000;95;false;false
+Catalytic activity;comment(CATALYTIC ACTIVITY);String;g8;50;1000;95;false;false
+Cofactor;comment(COFACTOR);String;g8;50;1000;95;false;false
+Enzyme regulation;comment(ENZYME REGULATION);String;g8;50;1000;95;false;false
+Function [CC];comment(FUNCTION);String;g8;50;1000;95;false;false
+Kinetics;comment(KINETICS);String;g8;50;1000;95;false;false
+Pathway;comment(PATHWAY);String;g8;50;1000;95;false;false
+Redox potential;comment(REDOX POTENTIAL);String;g8;50;1000;95;false;false
+Temperature dependence;comment(TEMPERATURE DEPENDENCE);String;g8;50;1000;95;false;false
+pH dependence;comment(PH DEPENDENCE);String;g8;50;1000;95;false;false
+Active site;feature(ACTIVE SITE);String;g8;50;1000;95;false;false
+Binding site;feature(BINDING SITE);String;g8;50;1000;95;false;false
+DNA binding;feature(DNA BINDING);String;g8;50;1000;95;false;false
+Metal binding;feature(METAL BINDING);String;g8;50;1000;95;false;false
+Nucleotide binding;feature(NP BIND);String;g8;50;1000;95;false;false
+Site;feature(SITE);String;g8;50;1000;95;false;false
+Annotation;annotation score;String;g6;50;1000;95;false;false
+Features;features;String;g6;50;1000;95;false;false
+Caution;comment(CAUTION);String;g6;50;1000;95;false;false
+Miscellaneous [CC];comment(GENERAL);String;g6;50;1000;95;false;false
+Keywords;keywords|keyword;String;g6;50;1000;95;false;true
+Protein existence;existence;String;g6;50;1000;95;false;true
+ALL;Search All;String;g7;50;1000;95;false;true;
+Subunit structure [CC];comment(SUBUNIT);String;g9;50;1000;95;false;false
+Interacts with;interactor;String;g9;50;1000;95;false;false
+Developmental stage;comment(DEVELOPMENTAL STAGE);String;g10;50;1000;95;false;false
+Induction;comment(INDUCTION);String;g10;50;1000;95;false;false
+Tissue specificity;comment(TISSUE SPECIFICITY);String;g10;50;1000;95;false;false
+Gene ontology (GO);go;String;g11;50;1000;95;false;true
+Gene ontology (biological process);go(biological process);String;g11;50;1000;95;false;false
+Gene ontology (molecular function);go(molecular function);String;g11;50;1000;95;false;false
+Gene ontology (cellular component);go(cellular component);String;g11;50;1000;95;false;false
+Gene ontology IDs;go-id;String;g11;50;1000;95;false;true
+Allergenic properties;comment(ALLERGEN);String;g12;50;1000;95;false;false
+Biotechnological use;comment(BIOTECHNOLOGY);String;g12;50;1000;95;false;false
+Disruption phenotype;comment(DISRUPTION PHENOTYPE);String;g12;50;1000;95;false;false
+Involvement in disease;comment(DISEASE);String;g12;50;1000;95;false;false
+Pharmaceutical use;comment(PHARMACEUTICAL);String;g12;50;1000;95;false;false
+Toxic dose;comment(TOXIC DOSE);String;g12;50;1000;95;false;false
+Subcellular location [CC];comment(SUBCELLULAR LOCATION);String;g13;50;1000;95;false;false
+Intramembrane;feature(INTRAMEMBRANE);String;g13;50;1000;95;false;false
+Topological domain;feature(TOPOLOGICAL DOMAIN);String;g13;50;1000;95;false;false
+Transmembrane;feature(TRANSMEMBRANE);String;g13;50;1000;95;false;false
+Post-translational modification;comment(PTM);String;g14;50;1000;95;false;false
+Chain;feature(CHAIN);String;g14;50;1000;95;false;false
+Cross-link;feature(CROSS LINK);String;g14;50;1000;95;false;false
+Disulfide bond;feature(DISULFIDE BOND);String;g14;50;1000;95;false;false
+Glycosylation;feature(GLYCOSYLATION);String;g14;50;1000;95;false;false
+Initiator methionine;feature(INITIATOR METHIONINE);String;g14;50;1000;95;false;false
+Lipidation;feature(LIPIDATION);String;g14;50;1000;95;false;false
+Modified residue;feature(MODIFIED RESIDUE);String;g14;50;1000;95;false;false
+Peptide;feature(PEPTIDE);String;g14;50;1000;95;false;false
+Propeptide;feature(PROPEPTIDE);String;g14;50;1000;95;false;false
+Signal peptide;feature(SIGNAL);String;g14;50;1000;95;false;false
+Transit peptide;feature(TRANSIT);String;g14;50;1000;95;false;false
+3D;3d;String;g15;50;1000;95;false;false
+Beta strand;feature(BETA STRAND);String;g15;50;1000;95;false;false
+Helix;feature(HELIX);String;g15;50;1000;95;false;false
+Turn;feature(TURN);String;g15;50;1000;95;false;false
+PubMed ID;citation;String;g16;50;1000;95;false;true
+Date of creation;created;String;g17;80;150;100;false;true
+Date of last modification;last-modified;String;g17;80;150;100;false;true
+Date of last sequence modification;sequence-modified;String;g17;80;150;100;false;true
+Version (entry);version(entry);int;g17;80;100;80;false;false
+Domain [cc];comment(DOMAIN)|domain;String;g18;80;1000;95;false;true
+Sequence similarities;comment(SIMILARITY);String;g18;50;1000;95;false;false
+Protein families;families|family;String;g18;50;1000;95;false;true
+Coiled coil;feature(COILED COIL);String;g18;50;1000;95;false;false
+Compositional bias;feature(COMPOSITIONAL BIAS);String;g18;50;1000;95;false;false
+Domain [FT];feature(DOMAIN EXTENT);String;g18;50;1000;95;false;false
+Motif;feature(MOTIF);String;g18;50;1000;95;false;false
+Region;feature(REGION);String;g18;50;1000;95;false;false
+Repeat;feature(REPEAT);String;g18;50;1000;95;false;false
+Zinc finger;feature(ZINC FINGER);String;g18;50;1000;95;false;false
+Cross-reference (EMBL);database(EMBL);String;g2;50;1000;95;false;false
+Cross-reference (PDB);database(PDB);String;g2;50;1000;95;false;false
+Cross-reference (ENSEMBL);database(ENSEMBL);String;g2;50;1000;95;false;false
+Cross-reference (PFAM);database(PFAM);String;g2;50;1000;95;false;false
+Cross-reference (RFAM);database(RFAM);String;g2;50;1000;95;false;false
+Cross-reference (CATH);database(CATH);String;g2;50;1000;95;false;false
+Cross-reference (SCOPE);database(SCOPE);String;g2;50;1000;95;false;false
+Cross-reference (GO);database(GO);String;g2;50;1000;95;false;false
+Cross-reference (INTERPRO);database(INTERPRO);String;g2;50;1000;95;false;false
+Mapped PubMed ID;citationmapping;String;g16;50;1000;95;false;true
+#
diff --git a/resources/images/blank_16x16_placeholder.png b/resources/images/blank_16x16_placeholder.png
new file mode 100644 (file)
index 0000000..885ad87
Binary files /dev/null and b/resources/images/blank_16x16_placeholder.png differ
index 05505a4..8dac5c6 100644 (file)
@@ -790,7 +790,7 @@ label.select_backgroud_colour = Select Background Colour
 label.invalid_font = Invalid Font
 label.separate_multiple_accession_ids = Enter one or more accession IDs separated by a semi-colon ";"
 label.separate_multiple_query_values = Enter one or more {0}s separated by a semi-colon ";"
-label.search_all = Enter one or more search values separated by a semi-colon ";" (Note: This Searches the entire PDB database)
+label.search_all = Enter one or more search values separated by a semi-colon ";" (Note: This Searches the entire database)
 label.replace_commas_semicolons = Replace commas with semi-colons
 label.parsing_failed_syntax_errors_shown_below_param = Parsing failed. Syntax errors shown below {0}
 label.parsing_failed_unrecoverable_exception_thrown_param = \nParsing failed. An unrecoverable exception was thrown\:\n {0}
@@ -801,6 +801,8 @@ label.wswublast_client_credits = To display sequence features an exact Uniprot i
 label.blasting_for_unidentified_sequence = BLASTing for unidentified sequences
 label.select_columns_containing = Select columns containing
 label.select_columns_not_containing = Select columns that do not contain
+label.hide_columns_containing = Hide columns containing
+label.hide_columns_not_containing = Hide columns that do not contain
 option.trim_retrieved_seqs = Trim retrieved sequences
 label.trim_retrieved_sequences = When the reference sequence is longer than the sequence that you are working with, only keep the relevant subsequences.
 label.use_sequence_id_1 = Use $SEQUENCE_ID$ or $SEQUENCE_ID=/<regex>/=$
@@ -1027,7 +1029,7 @@ error.implementation_error_need_to_have_httpresponse = Implementation Error: nee
 error.dbrefsource_implementation_exception =DBRefSource Implementation Exception
 error.implementation_error_dbinstance_must_implement_interface = Implmentation Error - getDbInstances must be given a class that implements jalview.ws.seqfetcher.DbSourceProxy (was given{0})
 error.implementation_error_must_init_dbsources =Implementation error. Must initialise dbSources
-label.view_controller_toggled_marked = {0} {1} columns {2} containing features of type {3}  across {4} sequence(s)
+label.view_controller_toggled_marked = {0} {1} columns containing features of type {2}  across {3} sequence(s)
 label.toggled = Toggled
 label.marked = Marked
 label.not = not
@@ -1241,7 +1243,7 @@ label.hide_insertions = Hide Insertions
 label.mark_as_representative = Mark as representative
 label.open_jabaws_web_page = Open JABAWS web page
 label.opens_the_jabaws_server_homepage = Opens the JABAWS server's homepage in web browser
-label.pdb_sequence_getcher = PDB Sequence Fetcher
+label.pdb_sequence_fetcher = PDB Sequence Fetcher
 label.result = result
 label.results = results
 label.structure_chooser = Structure Chooser
@@ -1279,10 +1281,10 @@ label.structure_chooser_filter_time = Structure Chooser - Filter time ({0})
 label.structure_chooser_no_of_structures = Structure Chooser - {0} Found ({1})
 info.no_pdb_entry_found_for = No PDB entry found for {0}
 exception.unable_to_detect_internet_connection = Jalview is unable to detect an internet connection
-exception.pdb_rest_service_no_longer_available = PDB rest services no longer available!
+exception.fts_rest_service_no_longer_available = {0} rest services no longer available!
 exception.resource_not_be_found = The requested resource could not be found
-exception.pdb_server_error = There seems to be an error from the PDB server
-exception.pdb_server_unreachable = Jalview is unable to reach the PDBe Solr server. \nPlease ensure that you are connected to the internet and try again.
+exception.fts_server_error = There seems to be an error from the {0} server
+exception.fts_server_unreachable = Jalview is unable to reach the {0} server. \nPlease ensure that you are connected to the internet and try again.
 label.nw_mapping = Needleman & Wunsch Alignment
 label.sifts_mapping = SIFTs Mapping
 label.mapping_method = Sequence \u27f7 Structure mapping method
@@ -1292,3 +1294,13 @@ status.cancelled_image_export_operation = Cancelled {0} export operation.
 info.error_creating_file = Error creating {0} file.
 exception.outofmemory_loading_mmcif_file = Out of memory loading mmCIF File
 info.error_creating_file = Error creating {0} file.
+label.run_groovy = Run Groovy console script
+label.run_groovy_tip = Run the script in the Groovy console over this alignment
+label.couldnt_run_groovy_script = Failed to run Groovy script
+label.uniprot_sequence_fetcher = UniProt Sequence Fetcher
+action.next_page= >> 
+action.prev_page= << 
+label.next_page_tooltop=Next Page
+label.prev_page_tooltop=Previous Page
+exception.bad_request=Bad request. There is a problem with your input.
+exception.service_not_available=Service not available. The server is being updated, try again later.
index 3535f4b..b3da568 100644 (file)
@@ -321,7 +321,7 @@ action.save_vamsas_session = Guardar Sesi
 label.select_vamsas_session_opened_as_new_vamsas_session= Selecciones una sesión vamsas para abrirla como una nueva sesión.
 label.open_saved_vamsas_session = Abrir una sesión VAMSAS guardada
 label.groovy_console = Consola Groovy 
-label.lineart = lineart
+label.lineart = Lineart
 label.dont_ask_me_again = No volver a preguntar
 label.select_eps_character_rendering_style = Seleccionar el carácter EPS como estilo de visualización 
 label.invert_selection = Invertir selección
@@ -962,7 +962,7 @@ error.implementation_error_need_to_have_httpresponse = Error de implementaci
 error.dbrefsource_implementation_exception = Excepción de implementación DBRefSource
 error.implementation_error_dbinstance_must_implement_interface = Error de Implementación- getDbInstances debe recibir una clase que implemente jalview.ws.seqfetcher.DbSourceProxy (recibió {0})
 error.implementation_error_must_init_dbsources =Error de implementación. Debe inicializar dbSources
-label.view_controller_toggled_marked = {0} {1} columnas {2} conteniendo características del tipo  {3} en {4} secuencia(s)
+label.view_controller_toggled_marked = {0} {1} columnas conteniendo características del tipo {2} en {3} secuencia(s)
 label.toggled = Invertida
 label.marked = Marcada
 label.not = no
index 108ccf1..3b84ee3 100755 (executable)
@@ -394,9 +394,12 @@ public class PDBChain
                 || ResidueProperties.nucleotideIndex[nucname
                         .charAt((deoxyn ? 1 : 0))] == -1)
         {
-          seq.append("X");
-          // System.err.println("PDBReader:Null aa3Hash for " +
-          // tmpat.resName);
+            char r = ResidueProperties
+                    .getSingleCharacterCode(ResidueProperties
+                            .getCanonicalAminoAcid(tmpat.resName));
+            seq.append(r == '0' ? 'X' : r);
+            // System.err.println("PDBReader:Null aa3Hash for " +
+            // tmpat.resName);
         }
         else
         {
index da744a4..e032c7a 100755 (executable)
@@ -62,7 +62,6 @@ import javax.swing.JMenuBar;
 import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
 import javax.swing.JRadioButtonMenuItem;
-import javax.swing.SwingUtilities;
 
 public class PDBViewer extends JInternalFrame implements Runnable
 {
@@ -416,8 +415,7 @@ public class PDBViewer extends JInternalFrame implements Runnable
           @Override
           public void mousePressed(MouseEvent evt)
           {
-            if (evt.isControlDown()
-                    || SwingUtilities.isRightMouseButton(evt))
+            if (evt.isPopupTrigger())
             {
               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
 
index 4201f43..c0c7c46 100644 (file)
@@ -199,8 +199,9 @@ public class ChimeraManager
       return null;
     }
 
-    List<ChimeraModel> newModelList = getModelList();
-    for (ChimeraModel newModel : newModelList)
+    // patch for Jalview - set model name in Chimera
+    // TODO: find a variant that works for sub-models
+    for (ChimeraModel newModel : getModelList())
     {
       if (!modelList.contains(newModel))
       {
@@ -209,15 +210,12 @@ public class ChimeraManager
                 "setattr M name " + modelName + " #"
                         + newModel.getModelNumber(), false);
         modelList.add(newModel);
-
       }
     }
 
     // assign color and residues to open models
     for (ChimeraModel chimeraModel : modelList)
     {
-      // // patch for Jalview - set model name in Chimera
-      // // TODO: find a variant that works for sub-models
       // get model color
       Color modelColor = getModelColor(chimeraModel);
       if (modelColor != null)
@@ -731,7 +729,7 @@ public class ChimeraManager
    */
   public List<String> sendChimeraCommand(String command, boolean reply)
   {
-    // System.out.println("chimeradebug>> " + command);
+   // System.out.println("chimeradebug>> " + command);
     if (!isChimeraLaunched() || command == null
             || "".equals(command.trim()))
     {
index 14e3907..fba023f 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import static jalview.io.gff.GffConstants.CLINICAL_SIGNIFICANCE;
+
 import jalview.datamodel.AlignedCodon;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
@@ -40,6 +42,8 @@ import jalview.util.MapList;
 import jalview.util.MappingUtils;
 import jalview.util.StringUtils;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -66,6 +70,31 @@ import java.util.TreeMap;
 public class AlignmentUtils
 {
 
+  private static final String SEQUENCE_VARIANT = "sequence_variant:";
+  private static final String ID = "ID";
+
+  /**
+   * A data model to hold the 'normal' base value at a position, and an optional
+   * sequence variant feature
+   */
+  static class DnaVariant
+  {
+    String base;
+
+    SequenceFeature variant;
+
+    DnaVariant(String nuc)
+    {
+      base = nuc;
+    }
+
+    DnaVariant(String nuc, SequenceFeature var)
+    {
+      base = nuc;
+      variant = var;
+    }
+  }
+
   /**
    * given an existing alignment, create a new alignment including all, or up to
    * flankSize additional symbols from each sequence's dataset sequence
@@ -1744,35 +1773,27 @@ public class AlignmentUtils
      * /ENSP00000288602?feature=transcript_variation;content-type=text/xml
      * which would be a bit slower but possibly more reliable
      */
-    LinkedHashMap<Integer, String[][]> variants = buildDnaVariantsMap(
+
+    /*
+     * build a map with codon variations for each potentially varying peptide
+     */
+    LinkedHashMap<Integer, List<DnaVariant>[]> variants = buildDnaVariantsMap(
             dnaSeq, dnaToProtein);
 
     /*
      * scan codon variations, compute peptide variants and add to peptide sequence
      */
     int count = 0;
-    for (Entry<Integer, String[][]> variant : variants.entrySet())
+    for (Entry<Integer, List<DnaVariant>[]> variant : variants.entrySet())
     {
       int peptidePos = variant.getKey();
-      String[][] codonVariants = variant.getValue();
-      String residue = String.valueOf(peptide.getCharAt(peptidePos - 1)); // 0-based
-      List<String> peptideVariants = computePeptideVariants(codonVariants,
-              residue);
-      if (!peptideVariants.isEmpty())
-      {
-        String desc = residue + "," // include canonical residue in description
-                + StringUtils.listToDelimitedString(peptideVariants, ", ");
-        SequenceFeature sf = new SequenceFeature(
-                SequenceOntologyI.SEQUENCE_VARIANT, desc, peptidePos,
-                peptidePos, 0f, null);
-        peptide.addSequenceFeature(sf);
-        count++;
-      }
+      List<DnaVariant>[] codonVariants = variant.getValue();
+      count += computePeptideVariants(peptide, peptidePos, codonVariants);
     }
 
     /*
-     * ugly sort to get sequence features in start position order
-     * - would be better to store in Sequence as a TreeSet instead?
+     * sort to get sequence features in start position order
+     * - would be better to store in Sequence as a TreeSet or NCList?
      */
     Arrays.sort(peptide.getSequenceFeatures(),
             new Comparator<SequenceFeature>()
@@ -1789,21 +1810,190 @@ public class AlignmentUtils
   }
 
   /**
-   * Builds a map whose key is position in the protein sequence, and value is an
-   * array of all variants for the coding codon positions
+   * Computes non-synonymous peptide variants from codon variants and adds them
+   * as sequence_variant features on the protein sequence (one feature per
+   * allele variant). Selected attributes (variant id, clinical significance)
+   * are copied over to the new features.
+   * 
+   * @param peptide
+   *          the protein sequence
+   * @param peptidePos
+   *          the position to compute peptide variants for
+   * @param codonVariants
+   *          a list of dna variants per codon position
+   * @return the number of features added
+   */
+  static int computePeptideVariants(SequenceI peptide, int peptidePos,
+          List<DnaVariant>[] codonVariants)
+  {
+    String residue = String.valueOf(peptide.getCharAt(peptidePos - 1));
+    int count = 0;
+    String base1 = codonVariants[0].get(0).base;
+    String base2 = codonVariants[1].get(0).base;
+    String base3 = codonVariants[2].get(0).base;
+
+    /*
+     * variants in first codon base
+     */
+    for (DnaVariant var : codonVariants[0])
+    {
+      if (var.variant != null)
+      {
+        String alleles = (String) var.variant.getValue("alleles");
+        if (alleles != null)
+        {
+          for (String base : alleles.split(","))
+          {
+            String codon = base + base2 + base3;
+            if (addPeptideVariant(peptide, peptidePos, residue, var, codon))
+            {
+              count++;
+            }
+          }
+        }
+      }
+    }
+
+    /*
+     * variants in second codon base
+     */
+    for (DnaVariant var : codonVariants[1])
+    {
+      if (var.variant != null)
+      {
+        String alleles = (String) var.variant.getValue("alleles");
+        if (alleles != null)
+        {
+          for (String base : alleles.split(","))
+          {
+            String codon = base1 + base + base3;
+            if (addPeptideVariant(peptide, peptidePos, residue, var, codon))
+            {
+              count++;
+            }
+          }
+        }
+      }
+    }
+
+    /*
+     * variants in third codon base
+     */
+    for (DnaVariant var : codonVariants[2])
+    {
+      if (var.variant != null)
+      {
+        String alleles = (String) var.variant.getValue("alleles");
+        if (alleles != null)
+        {
+          for (String base : alleles.split(","))
+          {
+            String codon = base1 + base2 + base;
+            if (addPeptideVariant(peptide, peptidePos, residue, var, codon))
+            {
+              count++;
+            }
+          }
+        }
+      }
+    }
+
+    return count;
+  }
+
+  /**
+   * Helper method that adds a peptide variant feature, provided the given codon
+   * translates to a value different to the current residue (is a non-synonymous
+   * variant). ID and clinical_significance attributes of the dna variant (if
+   * present) are copied to the new feature.
+   * 
+   * @param peptide
+   * @param peptidePos
+   * @param residue
+   * @param var
+   * @param codon
+   * @return true if a feature was added, else false
+   */
+  static boolean addPeptideVariant(SequenceI peptide, int peptidePos,
+          String residue, DnaVariant var, String codon)
+  {
+    /*
+     * get peptide translation of codon e.g. GAT -> D
+     * note that variants which are not single alleles,
+     * e.g. multibase variants or HGMD_MUTATION etc
+     * are currently ignored here
+     */
+    String trans = codon.contains("-") ? "-"
+            : (codon.length() > 3 ? null : ResidueProperties
+                    .codonTranslate(codon));
+    if (trans != null && !trans.equals(residue))
+    {
+      String residue3Char = StringUtils
+              .toSentenceCase(ResidueProperties.aa2Triplet.get(residue));
+      String trans3Char = StringUtils
+              .toSentenceCase(ResidueProperties.aa2Triplet.get(trans));
+      String desc = "p." + residue3Char + peptidePos + trans3Char;
+      // set score to 0f so 'graduated colour' option is offered! JAL-2060
+      SequenceFeature sf = new SequenceFeature(
+              SequenceOntologyI.SEQUENCE_VARIANT, desc, peptidePos,
+              peptidePos, 0f, "Jalview");
+      StringBuilder attributes = new StringBuilder(32);
+      String id = (String) var.variant.getValue(ID);
+      if (id != null)
+      {
+        if (id.startsWith(SEQUENCE_VARIANT))
+        {
+          id = id.substring(SEQUENCE_VARIANT.length());
+        }
+        sf.setValue(ID, id);
+        attributes.append(ID).append("=").append(id);
+        // TODO handle other species variants
+        StringBuilder link = new StringBuilder(32);
+        try
+        {
+          link.append(desc).append(" ").append(id)
+                  .append("|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=")
+                  .append(URLEncoder.encode(id, "UTF-8"));
+          sf.addLink(link.toString());
+        } catch (UnsupportedEncodingException e)
+        {
+          // as if
+        }
+      }
+      String clinSig = (String) var.variant
+              .getValue(CLINICAL_SIGNIFICANCE);
+      if (clinSig != null)
+      {
+        sf.setValue(CLINICAL_SIGNIFICANCE, clinSig);
+        attributes.append(";").append(CLINICAL_SIGNIFICANCE).append("=")
+                .append(clinSig);
+      }
+      peptide.addSequenceFeature(sf);
+      if (attributes.length() > 0)
+      {
+        sf.setAttributes(attributes.toString());
+      }
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Builds a map whose key is position in the protein sequence, and value is a
+   * list of the base and all variants for each corresponding codon position
    * 
    * @param dnaSeq
    * @param dnaToProtein
    * @return
    */
-  static LinkedHashMap<Integer, String[][]> buildDnaVariantsMap(
+  static LinkedHashMap<Integer, List<DnaVariant>[]> buildDnaVariantsMap(
           SequenceI dnaSeq, MapList dnaToProtein)
   {
     /*
-     * map from peptide position to all variant features of the codon for it
-     * LinkedHashMap ensures we add the peptide features in sequence order
+     * map from peptide position to all variants of the codon which codes for it
+     * LinkedHashMap ensures we keep the peptide features in sequence order
      */
-    LinkedHashMap<Integer, String[][]> variants = new LinkedHashMap<Integer, String[][]>();
+    LinkedHashMap<Integer, List<DnaVariant>[]> variants = new LinkedHashMap<Integer, List<DnaVariant>[]>();
     SequenceOntologyI so = SequenceOntologyFactory.getInstance();
 
     SequenceFeature[] dnaFeatures = dnaSeq.getSequenceFeatures();
@@ -1836,10 +2026,13 @@ public class AlignmentUtils
           continue;
         }
         int peptidePosition = mapsTo[0];
-        String[][] codonVariants = variants.get(peptidePosition);
+        List<DnaVariant>[] codonVariants = variants.get(peptidePosition);
         if (codonVariants == null)
         {
-          codonVariants = new String[3][];
+          codonVariants = new ArrayList[3];
+          codonVariants[0] = new ArrayList<DnaVariant>();
+          codonVariants[1] = new ArrayList<DnaVariant>();
+          codonVariants[2] = new ArrayList<DnaVariant>();
           variants.put(peptidePosition, codonVariants);
         }
 
@@ -1868,31 +2061,38 @@ public class AlignmentUtils
         lastCodon = codon;
 
         /*
-         * save nucleotide (and this variant) for each codon position
+         * save nucleotide (and any variant) for each codon position
          */
         for (int codonPos = 0; codonPos < 3; codonPos++)
         {
           String nucleotide = String.valueOf(
                   dnaSeq.getCharAt(codon[codonPos] - dnaStart))
                   .toUpperCase();
-          if (codonVariants[codonPos] == null)
+          List<DnaVariant> codonVariant = codonVariants[codonPos];
+          if (codon[codonPos] == dnaCol)
           {
-            /*
-             * record current dna base
-             */
-            codonVariants[codonPos] = new String[] { nucleotide };
+            if (!codonVariant.isEmpty()
+                    && codonVariant.get(0).variant == null)
+            {
+              /*
+               * already recorded base value, add this variant
+               */
+              codonVariant.get(0).variant = sf;
+            }
+            else
+            {
+              /*
+               * add variant with base value
+               */
+              codonVariant.add(new DnaVariant(nucleotide, sf));
+            }
           }
-          if (codon[codonPos] == dnaCol)
+          else if (codonVariant.isEmpty())
           {
             /*
-             * add alleles to dna base (and any previously found alleles)
+             * record (possibly non-varying) base value
              */
-            String[] known = codonVariants[codonPos];
-            String[] dnaVariants = new String[alleles.length + known.length];
-            System.arraycopy(known, 0, dnaVariants, 0, known.length);
-            System.arraycopy(alleles, 0, dnaVariants, known.length,
-                    alleles.length);
-            codonVariants[codonPos] = dnaVariants;
+            codonVariant.add(new DnaVariant(nucleotide));
           }
         }
       }
@@ -1901,71 +2101,6 @@ public class AlignmentUtils
   }
 
   /**
-   * Returns a sorted, non-redundant list of all peptide translations generated
-   * by the given dna variants, excluding the current residue value
-   * 
-   * @param codonVariants
-   *          an array of base values (acgtACGT) for codon positions 1, 2, 3
-   * @param residue
-   *          the current residue translation
-   * @return
-   */
-  static List<String> computePeptideVariants(String[][] codonVariants,
-          String residue)
-  {
-    List<String> result = new ArrayList<String>();
-    for (String base1 : codonVariants[0])
-    {
-      for (String base2 : codonVariants[1])
-      {
-        for (String base3 : codonVariants[2])
-        {
-          String codon = base1 + base2 + base3;
-          /*
-           * get peptide translation of codon e.g. GAT -> D
-           * note that variants which are not single alleles,
-           * e.g. multibase variants or HGMD_MUTATION etc
-           * are ignored here
-           */
-          String peptide = codon.contains("-") ? "-"
-                  : (codon.length() > 3 ? null : ResidueProperties
-                          .codonTranslate(codon));
-          if (peptide != null && !result.contains(peptide)
-                  && !peptide.equalsIgnoreCase(residue))
-          {
-            result.add(peptide);
-          }
-        }
-      }
-    }
-
-    /*
-     * sort alphabetically with STOP at the end
-     */
-    Collections.sort(result, new Comparator<String>()
-    {
-
-      @Override
-      public int compare(String o1, String o2)
-      {
-        if ("STOP".equals(o1))
-        {
-          return 1;
-        }
-        else if ("STOP".equals(o2))
-        {
-          return -1;
-        }
-        else
-        {
-          return o1.compareTo(o2);
-        }
-      }
-    });
-    return result;
-  }
-
-  /**
    * Makes an alignment with a copy of the given sequences, adding in any
    * non-redundant sequences which are mapped to by the cross-referenced
    * sequences.
index d4ae57d..7b3ce25 100755 (executable)
@@ -235,10 +235,7 @@ public class Conservation
               c = '-';
             }
 
-            if (!canonicaliseAa && 'a' <= c && c <= 'z')
-            {
-              c -= (32); // 32 = 'a' - 'A'
-            }
+            c = toUpperCase(c);
           }
           values[c]++;
         }
@@ -326,6 +323,7 @@ public class Conservation
       }
       else
       {
+        c = toUpperCase(c);
         nres++;
 
         if (nres == 1)
@@ -347,6 +345,22 @@ public class Conservation
   }
 
   /**
+   * Returns the upper-cased character if between 'a' and 'z', else the
+   * unchanged value
+   * 
+   * @param c
+   * @return
+   */
+  char toUpperCase(char c)
+  {
+    if ('a' <= c && c <= 'z')
+    {
+      c -= (32); // 32 = 'a' - 'A'
+    }
+    return c;
+  }
+
+  /**
    * Calculates the conservation sequence
    * 
    * @param consflag
index 3563eba..7e77fc1 100644 (file)
@@ -327,8 +327,8 @@ public class CrossRef
           }
           if (l > 0)
           {
-            System.out
-                    .println("Attempting to retrieve cross referenced sequences.");
+            // System.out
+            // .println("Attempting to retrieve cross referenced sequences.");
             DBRefEntry[] t = new DBRefEntry[l];
             l = 0;
             for (int r = 0; r < xrfs.length; r++)
index 40bedad..b0ecfde 100755 (executable)
@@ -59,13 +59,19 @@ public class SeqsetUtils
         sfeat.addElement(sfarray[i]);
       }
     }
-    sqinfo.put("SeqFeatures", sfeat);
-    sqinfo.put("PdbId",
+    if (seq.getDatasetSequence() == null)
+    {
+      sqinfo.put("SeqFeatures", sfeat);
+      sqinfo.put("PdbId",
             (seq.getAllPDBEntries() != null) ? seq.getAllPDBEntries()
                     : new Vector<PDBEntry>());
-    sqinfo.put("datasetSequence",
+    }
+    else
+    {
+      sqinfo.put("datasetSequence",
             (seq.getDatasetSequence() != null) ? seq.getDatasetSequence()
                     : new Sequence("THISISAPLACEHOLDER", ""));
+    }
     return sqinfo;
   }
 
@@ -129,6 +135,11 @@ public class SeqsetUtils
             && !(seqds.getName().equals("THISISAPLACEHOLDER") && seqds
                     .getLength() == 0))
     {
+      if (sfeatures!=null)
+      {
+        System.err
+                .println("Implementation error: setting dataset sequence for a sequence which has sequence features.\n\tDataset sequence features will not be visible.");
+      }
       sq.setDatasetSequence(seqds);
     }
 
index 1ca3342..7c81912 100644 (file)
@@ -23,9 +23,8 @@ package jalview.analysis.scoremodels;
 import jalview.api.analysis.ScoreModelI;
 import jalview.api.analysis.ViewBasedAnalysisI;
 import jalview.datamodel.AlignmentView;
+import jalview.datamodel.SeqCigar;
 import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.util.Comparison;
 
 import java.util.ArrayList;
 import java.util.Hashtable;
@@ -48,13 +47,10 @@ public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
   {
     int nofeats = 0;
     List<String> dft = fr.getDisplayedFeatureTypes();
-
     nofeats = dft.size();
-
-    SequenceI[] sequenceString = seqData.getVisibleAlignment(
-            Comparison.GapChars.charAt(0)).getSequencesArray();
-    int noseqs = sequenceString.length;
-    int cpwidth = seqData.getWidth();
+    SeqCigar[] seqs = seqData.getSequences();
+    int noseqs = seqs.length;
+    int cpwidth = 0;// = seqData.getWidth();
     float[][] distance = new float[noseqs][noseqs];
     if (nofeats == 0)
     {
@@ -67,54 +63,64 @@ public class FeatureScoreModel implements ScoreModelI, ViewBasedAnalysisI
       }
       return distance;
     }
-    float max = 0;
-    for (int cpos = 0; cpos < cpwidth; cpos++)
+    // need to get real position for view position
+    int[] viscont = seqData.getVisibleContigs();
+    for (int vc = 0; vc < viscont.length; vc += 2)
     {
-      // get visible features at cpos under view's display settings and compare
-      // them
-      List<Hashtable<String, SequenceFeature>> sfap = new ArrayList<Hashtable<String, SequenceFeature>>();
-      for (int i = 0; i < noseqs; i++)
-      {
-        Hashtable<String, SequenceFeature> types = new Hashtable<String, SequenceFeature>();
-        List<SequenceFeature> sfs = fr.findFeaturesAtRes(sequenceString[i],
-                sequenceString[i].findPosition(cpos));
-        for (SequenceFeature sf : sfs)
-        {
-          types.put(sf.getType(), sf);
-        }
-        sfap.add(types);
-      }
-      for (int i = 0; i < (noseqs - 1); i++)
+
+      for (int cpos = viscont[vc]; cpos <= viscont[vc + 1]; cpos++)
       {
-        if (cpos == 0)
-        {
-          distance[i][i] = 0f;
-        }
-        for (int j = i + 1; j < noseqs; j++)
+        cpwidth++;
+        // get visible features at cpos under view's display settings and
+        // compare them
+        List<Hashtable<String, SequenceFeature>> sfap = new ArrayList<Hashtable<String, SequenceFeature>>();
+        for (int i = 0; i < noseqs; i++)
         {
-          int sfcommon = 0;
-          // compare the two lists of features...
-          Hashtable<String, SequenceFeature> fi = sfap.get(i), fk, fj = sfap
-                  .get(j);
-          if (fi.size() > fj.size())
+          Hashtable<String, SequenceFeature> types = new Hashtable<String, SequenceFeature>();
+          int spos = seqs[i].findPosition(cpos);
+          if (spos != -1)
           {
-            fk = fj;
+            List<SequenceFeature> sfs = fr.findFeaturesAtRes(
+                    seqs[i].getRefSeq(), spos);
+            for (SequenceFeature sf : sfs)
+            {
+              types.put(sf.getType(), sf);
+            }
           }
-          else
+          sfap.add(types);
+        }
+        for (int i = 0; i < (noseqs - 1); i++)
+        {
+          if (cpos == 0)
           {
-            fk = fi;
-            fi = fj;
+            distance[i][i] = 0f;
           }
-          for (String k : fi.keySet())
+          for (int j = i + 1; j < noseqs; j++)
           {
-            SequenceFeature sfj = fk.get(k);
-            if (sfj != null)
+            int sfcommon = 0;
+            // compare the two lists of features...
+            Hashtable<String, SequenceFeature> fi = sfap.get(i), fk, fj = sfap
+                    .get(j);
+            if (fi.size() > fj.size())
+            {
+              fk = fj;
+            }
+            else
+            {
+              fk = fi;
+              fi = fj;
+            }
+            for (String k : fi.keySet())
             {
-              sfcommon++;
+              SequenceFeature sfj = fk.get(k);
+              if (sfj != null)
+              {
+                sfcommon++;
+              }
             }
+            distance[i][j] += (fi.size() + fk.size() - 2f * sfcommon);
+            distance[j][i] += distance[i][j];
           }
-          distance[i][j] += (fi.size() + fk.size() - 2f * sfcommon);
-          distance[j][i] += distance[i][j];
         }
       }
     }
index 872528b..06dc054 100644 (file)
@@ -22,12 +22,30 @@ package jalview.api;
 
 import jalview.datamodel.AlignmentAnnotation;
 
+/**
+ * Interface describing a worker that calculates alignment annotation(s). The
+ * main (re-)calculation should be performed by the inherited run() method.
+ */
 public interface AlignCalcWorkerI extends Runnable
 {
-
+  /**
+   * Answers true if this worker updates the given annotation (regardless of its
+   * current state)
+   * 
+   * @param annot
+   * @return
+   */
   public boolean involves(AlignmentAnnotation annot);
 
+  /**
+   * Updates the display of calculated annotation values (does not recalculate
+   * the values). This allows for quick redraw of annotations when display
+   * settings are changed.
+   */
   public void updateAnnotation();
 
-  void removeOurAnnotation();
+  /**
+   * Removes any annotation managed by this worker from the alignment
+   */
+  void removeAnnotation();
 }
index 17a1563..26966ba 100644 (file)
@@ -64,7 +64,7 @@ public interface AlignViewControllerI
    * @return true if operation affected state
    */
   boolean markColumnsContainingFeatures(boolean invert,
-          boolean extendCurrent, boolean clearColumns, String featureType);
+          boolean extendCurrent, boolean toggle, String featureType);
 
   /**
    * sort the alignment or current selection by average score over the given set
index 9415745..490b21a 100644 (file)
@@ -2,6 +2,7 @@ package jalview.api;
 
 import jalview.datamodel.Mapping;
 
+//JBPComment: this is a datamodel API - so it should be in datamodel (it's a peer of SequenceI)
 
 public interface DBRefEntryI
 {
index 5b15cad..d9795a6 100644 (file)
@@ -143,12 +143,6 @@ public interface FeatureRenderer
   List<SequenceFeature> findFeaturesAtRes(SequenceI sequence, int res);
 
   /**
-   * 
-   * @return true if the rendering platform supports transparency
-   */
-  boolean isTransparencyAvailable();
-
-  /**
    * get current displayed types, in ordering of rendering (on top last)
    * 
    * @return a (possibly empty) list of feature types
index e2fac14..527ae17 100644 (file)
@@ -29,6 +29,8 @@ import jalview.xml.binding.sifts.Entry.Entity;
 import java.util.HashMap;
 import java.util.HashSet;
 
+// JBPComment: this isn't a top-level Jalview API - should be in its own package api
+
 public interface SiftsClientI
 {
   /**
index 4391fa2..a93cdcb 100644 (file)
@@ -63,9 +63,6 @@ public class FeatureRenderer extends
   {
     super();
     this.av = av;
-
-    setTransparencyAvailable(!System.getProperty("java.version")
-            .startsWith("1.1"));
   }
 
   static String lastFeatureAdded;
@@ -354,12 +351,6 @@ public class FeatureRenderer extends
     start.setText(features[0].getBegin() + "");
     end.setText(features[0].getEnd() + "");
     description.setText(features[0].getDescription());
-    Color col = getColour(name.getText());
-    if (col == null)
-    {
-      col = new jalview.schemes.UserColourScheme()
-              .createColourFromName(name.getText());
-    }
     Object fcol = getFeatureStyle(name.getText());
     // simply display the feature color in a box
     colourPanel.updateColor(fcol);
index 584a69a..7ae318c 100755 (executable)
@@ -59,8 +59,8 @@ import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.util.Arrays;
 import java.util.Enumeration;
-import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
 public class FeatureSettings extends Panel implements ItemListener,
@@ -95,14 +95,7 @@ public class FeatureSettings extends Panel implements ItemListener,
     transparency = new Scrollbar(Scrollbar.HORIZONTAL,
             100 - (int) (fr.getTransparency() * 100), 1, 1, 100);
 
-    if (fr.isTransparencyAvailable())
-    {
-      transparency.addAdjustmentListener(this);
-    }
-    else
-    {
-      transparency.setEnabled(false);
-    }
+    transparency.addAdjustmentListener(this);
 
     java.net.URL url = getClass().getResource("/images/link.gif");
     if (url != null)
@@ -134,17 +127,8 @@ public class FeatureSettings extends Panel implements ItemListener,
 
     Panel tPanel = new Panel(new BorderLayout());
 
-    if (fr.isTransparencyAvailable())
-    {
-      tPanel.add(transparency, BorderLayout.CENTER);
-      tPanel.add(new Label("Transparency"), BorderLayout.EAST);
-    }
-    else
-    {
-      tPanel.add(
-              new Label("Transparency not available in this web browser"),
-              BorderLayout.CENTER);
-    }
+    tPanel.add(transparency, BorderLayout.CENTER);
+    tPanel.add(new Label("Transparency"), BorderLayout.EAST);
 
     lowerPanel.add(tPanel, BorderLayout.SOUTH);
 
@@ -201,7 +185,8 @@ public class FeatureSettings extends Panel implements ItemListener,
             60);
   }
 
-  protected void popupSort(final MyCheckbox check, final Hashtable minmax,
+  protected void popupSort(final MyCheckbox check,
+          final Map<String, float[][]> minmax,
           int x, int y)
   {
     final String type = check.type;
@@ -237,9 +222,10 @@ public class FeatureSettings extends Panel implements ItemListener,
 
     });
     men.add(dens);
+
     if (minmax != null)
     {
-      final Object typeMinMax = minmax.get(type);
+      final float[][] typeMinMax = minmax.get(type);
       /*
        * final java.awt.CheckboxMenuItem chb = new
        * java.awt.CheckboxMenuItem("Vary Height"); // this is broken at the
@@ -252,7 +238,7 @@ public class FeatureSettings extends Panel implements ItemListener,
        * 
        * }); men.add(chb);
        */
-      if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
+      if (typeMinMax != null && typeMinMax[0] != null)
       {
         // graduated colourschemes for those where minmax exists for the
         // positional features
@@ -282,6 +268,57 @@ public class FeatureSettings extends Panel implements ItemListener,
         });
       }
     }
+
+    MenuItem selectContaining = new MenuItem(
+            MessageManager.getString("label.select_columns_containing"));
+    selectContaining.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        me.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
+                false, type);
+      }
+    });
+    men.add(selectContaining);
+
+    MenuItem selectNotContaining = new MenuItem(
+            MessageManager.getString("label.select_columns_not_containing"));
+    selectNotContaining.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        me.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
+                false, type);
+      }
+    });
+    men.add(selectNotContaining);
+
+    MenuItem hideContaining = new MenuItem(
+            MessageManager.getString("label.hide_columns_containing"));
+    hideContaining.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hideFeatureColumns(type, true);
+      }
+    });
+    men.add(hideContaining);
+
+    MenuItem hideNotContaining = new MenuItem(
+            MessageManager.getString("label.hide_columns_not_containing"));
+    hideNotContaining.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        hideFeatureColumns(type, false);
+      }
+    });
+    men.add(hideNotContaining);
+
     this.featurePanel.add(men);
     men.show(this.featurePanel, x, y);
   }
@@ -338,7 +375,7 @@ public class FeatureSettings extends Panel implements ItemListener,
   {
     SequenceFeature[] tmpfeatures;
     String group = null, type;
-    Vector visibleChecks = new Vector();
+    Vector<String> visibleChecks = new Vector<String>();
     AlignmentI alignment = av.getAlignment();
     for (int i = 0; i < alignment.getHeight(); i++)
     {
@@ -404,7 +441,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
     // now add checkboxes which should be visible,
     // if they have not already been added
-    Enumeration en = visibleChecks.elements();
+    Enumeration<String> en = visibleChecks.elements();
 
     while (en.hasMoreElements())
     {
@@ -793,4 +830,24 @@ public class FeatureSettings extends Panel implements ItemListener,
   {
   }
 
+  /**
+   * Hide columns containing (or not containing) a given feature type
+   * 
+   * @param type
+   * @param columnsContaining
+   */
+  void hideFeatureColumns(final String type,
+          boolean columnsContaining)
+  {
+    if (ap.alignFrame.avc.markColumnsContainingFeatures(
+            columnsContaining, false, false, type))
+    {
+      if (ap.alignFrame.avc.markColumnsContainingFeatures(
+              !columnsContaining, false, false, type))
+      {
+        ap.alignFrame.viewport.hideSelectedColumns();
+      }
+    }
+  }
+
 }
index 16bba0b..d72e91f 100755 (executable)
@@ -138,11 +138,13 @@ public class IdCanvas extends Panel
     repaint();
   }
 
+  @Override
   public void update(Graphics g)
   {
     paint(g);
   }
 
+  @Override
   public void paint(Graphics g)
   {
     if (getSize().height < 0 || getSize().width < 0)
@@ -378,7 +380,7 @@ public class IdCanvas extends Panel
     Font bold = new Font(av.getFont().getName(), Font.BOLD, av.getFont()
             .getSize());
 
-    if (av.isHiddenRepSequence(seq))
+    if (av.isReferenceSeq(seq) || av.isHiddenRepSequence(seq))
     {
       gg.setFont(bold);
       return true;
index a8397ba..9ec1ce4 100644 (file)
@@ -1990,7 +1990,6 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
             av);
     av.setColumnSelection(cs);
-    av.isColSelChanged(true);
 
     ap.scalePanelHolder.repaint();
     ap.repaint();
index 7911cd5..38aa55f 100755 (executable)
@@ -426,7 +426,7 @@ public class Cache
     System.out
             .println("Jalview Version: " + codeVersion + codeInstallation);
 
-    Pdb.setCurrentDefaultFomart(jalview.bin.Cache.getDefault(
+    Pdb.setCurrentDefaultFormat(jalview.bin.Cache.getDefault(
             "DEFAULT_STRUCTURE_FORMAT", DEFAULT_STRUCTURE_FORMAT));
     // jnlpVersion will be null if we're using InstallAnywhere
     // Dont do this check if running in headless mode
index 8fe3bca..d0b3232 100755 (executable)
@@ -51,7 +51,6 @@ import java.util.Map;
 import java.util.Vector;
 
 import javax.swing.UIManager;
-import javax.swing.UnsupportedLookAndFeelException;
 
 /**
  * Main class for Jalview Application <br>
@@ -68,6 +67,7 @@ public class Jalview
     // grab all the rights we can the JVM
     Policy.setPolicy(new Policy()
     {
+      @Override
       public PermissionCollection getPermissions(CodeSource codesource)
       {
         Permissions perms = new Permissions();
@@ -75,6 +75,7 @@ public class Jalview
         return (perms);
       }
 
+      @Override
       public void refresh()
       {
       }
@@ -89,6 +90,7 @@ public class Jalview
    */
   public static void main(String[] args)
   {
+    System.setSecurityManager(null);
     System.out.println("Java version: "
             + System.getProperty("java.version"));
     System.out.println(System.getProperty("os.arch") + " "
@@ -178,7 +180,7 @@ public class Jalview
     } catch (Exception ex)
     {
     }
-    if (new Platform().isAMac())
+    if (Platform.isAMac())
     {
       System.setProperty("com.apple.mrj.application.apple.menu.about.name",
               "Jalview");
@@ -187,10 +189,10 @@ public class Jalview
       {
         UIManager.setLookAndFeel(ch.randelshofer.quaqua.QuaquaManager
                 .getLookAndFeel());
-      } catch (UnsupportedLookAndFeelException e)
+      } catch (Throwable e)
       {
-        // TODO Auto-generated catch block
-        e.printStackTrace();
+        System.err.println("Failed to set QuaQua look and feel: "
+                + e.toString());
       }
     }
 
@@ -514,20 +516,10 @@ public class Jalview
         {
           // Execute the groovy script after we've done all the rendering stuff
           // and before any images or figures are generated.
-          if (jalview.bin.Cache.groovyJarsPresent())
-          {
-            System.out.println("Executing script " + groovyscript);
-            executeGroovyScript(groovyscript, new Object[] { desktop, af });
-
-            System.out.println("CMD groovy[" + groovyscript
-                    + "] executed successfully!");
-          }
-          else
-          {
-            System.err
-                    .println("Sorry. Groovy Support is not available, so ignoring the provided groovy script "
-                            + groovyscript);
-          }
+          System.out.println("Executing script " + groovyscript);
+          executeGroovyScript(groovyscript, new Object[] { desktop, af });
+          System.out.println("CMD groovy[" + groovyscript
+                  + "] executed successfully!");
           groovyscript = null;
         }
         String imageName = "unnamed.png";
@@ -729,6 +721,7 @@ public class Jalview
                     + "\n\n(you can enable or disable usage tracking in the preferences)",
             new Runnable()
             {
+              @Override
               public void run()
               {
                 Cache.log
@@ -738,6 +731,7 @@ public class Jalview
               }
             }, new Runnable()
             {
+              @Override
               public void run()
               {
                 Cache.log.debug("Not enabling Google Tracking.");
@@ -1040,6 +1034,7 @@ public class Jalview
 
 class rnabuttonlistener implements ActionListener
 {
+  @Override
   public void actionPerformed(ActionEvent arg0)
   {
     System.out.println("Good idea ! ");
@@ -1049,6 +1044,7 @@ class rnabuttonlistener implements ActionListener
 
 class pbuttonlistener implements ActionListener
 {
+  @Override
   public void actionPerformed(ActionEvent arg0)
   {
 
@@ -1169,6 +1165,7 @@ class FeatureFetcher
     new Thread(new Runnable()
     {
 
+      @Override
       public void run()
       {
         synchronized (us)
index ca2ae6d..24439ca 100644 (file)
@@ -238,6 +238,7 @@ public class AlignViewController implements AlignViewControllerI
     ColumnSelection cs = viewport.getColumnSelection();
     if (bs.cardinality() > 0 || invert)
     {
+      boolean changed = false;
       if (cs == null)
       {
         cs = new ColumnSelection();
@@ -246,6 +247,7 @@ public class AlignViewController implements AlignViewControllerI
       {
         if (!extendCurrent)
         {
+          changed = !cs.isEmpty();
           cs.clear();
         }
       }
@@ -257,6 +259,7 @@ public class AlignViewController implements AlignViewControllerI
         {
           if (ibs < 0 || i < ibs)
           {
+            changed = true;
             if (toggle && cs.contains(i))
             {
               cs.removeElement(i++);
@@ -278,6 +281,7 @@ public class AlignViewController implements AlignViewControllerI
         for (int i = bs.nextSetBit(alStart); i >= alStart; i = bs
                 .nextSetBit(i + 1))
         {
+          changed = true;
           if (toggle && cs.contains(i))
           {
             cs.removeElement(i);
@@ -288,18 +292,21 @@ public class AlignViewController implements AlignViewControllerI
           }
         }
       }
-      viewport.setColumnSelection(cs);
-      alignPanel.paintAlignment(true);
-      avcg.setStatus(MessageManager.formatMessage(
-              "label.view_controller_toggled_marked",
-              new String[] {
-                  (toggle ? MessageManager.getString("label.toggled")
-                          : MessageManager.getString("label.marked")),
-                  (invert ? (Integer.valueOf((alw - alStart)
-                          - bs.cardinality()).toString()) : (Integer
-                          .valueOf(bs.cardinality()).toString())),
-                  featureType, Integer.valueOf(nseq).toString() }));
-      return true;
+      if (changed)
+      {
+        viewport.setColumnSelection(cs);
+        alignPanel.paintAlignment(true);
+        avcg.setStatus(MessageManager.formatMessage(
+                "label.view_controller_toggled_marked",
+                new String[] {
+                    (toggle ? MessageManager.getString("label.toggled")
+                            : MessageManager.getString("label.marked")),
+                    (invert ? (Integer.valueOf((alw - alStart)
+                            - bs.cardinality()).toString()) : (Integer
+                            .valueOf(bs.cardinality()).toString())),
+                    featureType, Integer.valueOf(nseq).toString() }));
+        return true;
+      }
     }
     else
     {
@@ -311,8 +318,8 @@ public class AlignViewController implements AlignViewControllerI
         cs.clear();
         alignPanel.paintAlignment(true);
       }
-      return false;
     }
+    return false;
   }
 
   @Override
index d1ea70d..a9b0d53 100755 (executable)
@@ -1793,4 +1793,40 @@ public class Alignment implements AlignmentI
     }
     return null;
   }
+
+  @Override
+  public int[] getVisibleStartAndEndIndex(List<int[]> hiddenCols)
+  {
+    int[] alignmentStartEnd = new int[] { 0, getWidth() - 1 };
+    int startPos = alignmentStartEnd[0];
+    int endPos = alignmentStartEnd[1];
+
+    int[] lowestRange = new int[] { -1, -1 };
+    int[] higestRange = new int[] { -1, -1 };
+
+    for (int[] hiddenCol : hiddenCols)
+    {
+      lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
+      higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
+    }
+
+    if (lowestRange[0] == -1 && lowestRange[1] == -1)
+    {
+      startPos = alignmentStartEnd[0];
+    }
+    else
+    {
+      startPos = lowestRange[1] + 1;
+    }
+
+    if (higestRange[0] == -1 && higestRange[1] == -1)
+    {
+      endPos = alignmentStartEnd[1];
+    }
+    else
+    {
+      endPos = higestRange[0] - 1;
+    }
+    return new int[] { startPos, endPos };
+  }
 }
index 396ef2d..76d1a48 100755 (executable)
@@ -543,4 +543,13 @@ public interface AlignmentI extends AnnotatedCollectionI
    * @return
    */
   AlignedCodonFrame getMapping(SequenceI mapFrom, SequenceI mapTo);
+
+  /**
+   * Calculate the visible start and end index of an alignment. The result is
+   * returned an int array where: int[0] = startIndex, and int[1] = endIndex.
+   * 
+   * @param hiddenCols
+   * @return
+   */
+  public int[] getVisibleStartAndEndIndex(List<int[]> hiddenCols);
 }
index c23b772..74c58b7 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.datamodel;
 
+import jalview.util.Comparison;
 import jalview.util.ShiftList;
 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.SearchableAnnotationField;
@@ -31,10 +32,14 @@ import java.util.List;
 import java.util.Vector;
 
 /**
- * NOTE: Columns are zero based.
+ * Data class holding the selected columns and hidden column ranges for a view.
+ * Ranges are base 1.
  */
 public class ColumnSelection
 {
+  /**
+   * A class to hold an efficient representation of selected columns
+   */
   private class IntList
   {
     /*
@@ -204,9 +209,27 @@ public class ColumnSelection
       }
       return rlist;
     }
+
+    @Override
+    public int hashCode()
+    {
+      // TODO Auto-generated method stub
+      return selected.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+      if (obj instanceof IntList)
+      {
+        return ((IntList) obj).selected.equals(selected);
+      }
+      return false;
+    }
   }
 
-  IntList selected = new IntList();
+  IntList selection = new IntList();
+
   /*
    * list of hidden column [start, end] ranges; the list is maintained in
    * ascending start column order
@@ -221,7 +244,7 @@ public class ColumnSelection
    */
   public void addElement(int col)
   {
-    selected.add(col);
+    selection.add(col);
   }
 
   /**
@@ -229,7 +252,7 @@ public class ColumnSelection
    */
   public void clear()
   {
-    selected.clear();
+    selection.clear();
   }
 
   /**
@@ -240,7 +263,7 @@ public class ColumnSelection
    */
   public void removeElement(int col)
   {
-    selected.remove(col);
+    selection.remove(col);
   }
 
   /**
@@ -257,9 +280,9 @@ public class ColumnSelection
     for (int i = start; i < end; i++)
     {
       colInt = new Integer(i);
-      if (selected.contains(colInt))
+      if (selection.contains(colInt))
       {
-        selected.remove(colInt);
+        selection.remove(colInt);
       }
     }
   }
@@ -271,7 +294,7 @@ public class ColumnSelection
    */
   public List<Integer> getSelected()
   {
-    return selected.getList();
+    return selection.getList();
   }
 
   /**
@@ -280,7 +303,7 @@ public class ColumnSelection
    */
   public List<int[]> getSelectedRanges()
   {
-    return selected.getRanges();
+    return selection.getRanges();
   }
 
   /**
@@ -292,7 +315,7 @@ public class ColumnSelection
    */
   public boolean contains(int col)
   {
-    return (col > -1) ? selected.isSelected(col) : false;
+    return (col > -1) ? selection.isSelected(col) : false;
   }
 
   /**
@@ -300,7 +323,7 @@ public class ColumnSelection
    */
   public boolean isEmpty()
   {
-    return selected == null || selected.isEmpty();
+    return selection == null || selection.isEmpty();
   }
 
   /**
@@ -310,11 +333,11 @@ public class ColumnSelection
    */
   public int getMax()
   {
-    if (selected.isEmpty())
+    if (selection.isEmpty())
     {
       return -1;
     }
-    return selected.getMaxColumn();
+    return selection.getMaxColumn();
   }
 
   /**
@@ -324,11 +347,11 @@ public class ColumnSelection
    */
   public int getMin()
   {
-    if (selected.isEmpty())
+    if (selection.isEmpty())
     {
       return 1000000000;
     }
-    return selected.getMinColumn();
+    return selection.getMinColumn();
   }
 
   /**
@@ -342,7 +365,7 @@ public class ColumnSelection
   public List<int[]> compensateForEdit(int start, int change)
   {
     List<int[]> deletedHiddenColumns = null;
-    selected.compensateForEdits(start, change);
+    selection.compensateForEdits(start, change);
 
     if (hiddenColumns != null)
     {
@@ -392,7 +415,7 @@ public class ColumnSelection
   private void compensateForDelEdits(int start, int change)
   {
 
-    selected.compensateForEdits(start, change);
+    selection.compensateForEdits(start, change);
 
     if (hiddenColumns != null)
     {
@@ -569,12 +592,12 @@ public class ColumnSelection
             hiddenColumns = null;
           }
         }
-        if (selected != null && selected.size() > 0)
+        if (selection != null && selection.size() > 0)
         {
-          selected.pruneColumnList(shifts);
-          if (selected != null && selected.size() == 0)
+          selection.pruneColumnList(shifts);
+          if (selection != null && selection.size() == 0)
           {
-            selected = null;
+            selection = null;
           }
         }
         // and shift the rest.
@@ -598,7 +621,7 @@ public class ColumnSelection
    * Return absolute column index for a visible column index
    * 
    * @param column
-   *          int column index in alignment view
+   *          int column index in alignment view (count from zero)
    * @return alignment column index for column
    */
   public int adjustForHiddenColumns(int column)
@@ -741,12 +764,13 @@ public class ColumnSelection
 
   public void hideSelectedColumns()
   {
-    synchronized (selected) {
-      for (int[] selregions:selected.getRanges())
+    synchronized (selection)
+    {
+      for (int[] selregions : selection.getRanges())
       {
         hideColumns(selregions[0], selregions[1]);
       }
-      selected.clear();
+      selection.clear();
     }
 
   }
@@ -806,7 +830,7 @@ public class ColumnSelection
     /*
      * remaining case is that the new range follows everything else
      */
-      hiddenColumns.addElement(new int[] { start, end });
+    hiddenColumns.addElement(new int[] { start, end });
   }
 
   /**
@@ -924,12 +948,12 @@ public class ColumnSelection
   {
     if (copy != null)
     {
-      if (copy.selected != null)
+      if (copy.selection != null)
       {
-        selected = new IntList();
-        for (int i = 0, j = copy.selected.size(); i < j; i++)
+        selection = new IntList();
+        for (int i = 0, j = copy.selection.size(); i < j; i++)
         {
-          selected.add(copy.selected.elementAt(i));
+          selection.add(copy.selection.elementAt(i));
         }
       }
       if (copy.hiddenColumns != null)
@@ -1077,6 +1101,84 @@ public class ColumnSelection
   }
 
   /**
+   * Locate the first and last position visible for this sequence. if seq isn't
+   * visible then return the position of the left and right of the hidden
+   * boundary region, and the corresponding alignment column indices for the
+   * extent of the sequence
+   * 
+   * @param seq
+   * @return int[] { visible start, visible end, first seqpos, last seqpos,
+   *         alignment index for seq start, alignment index for seq end }
+   */
+  public int[] locateVisibleBoundsOfSequence(SequenceI seq)
+  {
+    int fpos=seq.getStart(),lpos= seq.getEnd();
+    int start = 0;
+    
+    if (hiddenColumns == null || hiddenColumns.size() == 0)
+    {
+      int ifpos = seq.findIndex(fpos) - 1, ilpos = seq.findIndex(lpos) - 1;
+      return new int[] { ifpos, ilpos, fpos, lpos, ifpos, ilpos };
+    }
+
+    // Simply walk along the sequence whilst watching for hidden column
+    // boundaries
+    List<int[]> regions = getHiddenColumns();
+    int spos = fpos, lastvispos = -1, rcount = 0, hideStart = seq
+            .getLength(), hideEnd = -1;
+    int visPrev = 0, visNext = 0, firstP = -1, lastP = -1;
+    boolean foundStart = false;
+    for (int p = 0, pLen = seq.getLength(); spos <= seq.getEnd()
+            && p < pLen; p++)
+    {
+      if (!Comparison.isGap(seq.getCharAt(p)))
+      {
+        // keep track of first/last column
+        // containing sequence data regardless of visibility
+        if (firstP == -1)
+        {
+          firstP = p;
+        }
+        lastP = p;
+        // update hidden region start/end
+        while (hideEnd < p && rcount < regions.size())
+        {
+          int[] region = regions.get(rcount++);
+          visPrev = visNext;
+          visNext += region[0] - visPrev;
+          hideStart = region[0];
+          hideEnd = region[1];
+        }
+        if (hideEnd < p)
+        {
+          hideStart = seq.getLength();
+        }
+        // update visible boundary for sequence
+        if (p < hideStart)
+        {
+          if (!foundStart)
+          {
+            fpos = spos;
+            start = p;
+            foundStart = true;
+          }
+          lastvispos = p;
+          lpos = spos;
+        }
+        // look for next sequence position
+        spos++;
+      }
+    }
+    if (foundStart)
+    {
+      return new int[] { findColumnPosition(start),
+          findColumnPosition(lastvispos), fpos, lpos, firstP, lastP };
+    }
+    // otherwise, sequence was completely hidden
+    return new int[] { visPrev, visNext, 0, 0, firstP, lastP };
+  }
+
+  /**
    * delete any columns in alignmentAnnotation that are hidden (including
    * sequence associated annotation).
    * 
@@ -1226,7 +1328,7 @@ public class ColumnSelection
       {
         if (hiddenColumns != null && isVisible(col.intValue()))
         {
-          selected.add(col);
+          selection.add(col);
         }
       }
     }
@@ -1240,8 +1342,8 @@ public class ColumnSelection
    */
   public void setElementsFrom(ColumnSelection colsel)
   {
-    selected = new IntList();
-    if (colsel.selected != null && colsel.selected.size() > 0)
+    selection = new IntList();
+    if (colsel.selection != null && colsel.selection.size() > 0)
     {
       if (hiddenColumns != null && hiddenColumns.size() > 0)
       {
@@ -1408,7 +1510,7 @@ public class ColumnSelection
    */
   public boolean hasSelectedColumns()
   {
-    return (selected != null && selected.size() > 0);
+    return (selection != null && selection.size() > 0);
   }
 
   /**
@@ -1447,6 +1549,8 @@ public class ColumnSelection
   public boolean filterAnnotations(Annotation[] annotations,
           AnnotationFilterParameter filterParams)
   {
+    // JBPNote - this method needs to be refactored to become independent of
+    // viewmodel package
     this.revealAllHiddenColumns();
     this.clear();
     int count = 0;
@@ -1528,4 +1632,33 @@ public class ColumnSelection
     } while (count < annotations.length);
     return false;
   }
+
+  /**
+   * Returns a hashCode built from selected columns and hidden column ranges
+   */
+  @Override
+  public int hashCode()
+  {
+    int hashCode = selection.hashCode();
+    if (hiddenColumns != null)
+    {
+      for (int[] hidden : hiddenColumns)
+      {
+        hashCode = 31 * hashCode + hidden[0];
+        hashCode = 31 * hashCode + hidden[1];
+      }
+    }
+    return hashCode;
+  }
+
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (obj instanceof ColumnSelection)
+    {
+      return hashCode() == obj.hashCode();
+    }
+    return false;
+  }
+
 }
index 91b49eb..a2243be 100755 (executable)
@@ -88,6 +88,8 @@ public class DBRefSource
    */
   public static final String ENSEMBL = "ENSEMBL";
 
+  public static final String ENSEMBLGENOMES = "ENSEMBLGENOMES";
+
   /**
    * List of databases whose sequences might have coding regions annotated
    */
index d279a26..98b0de5 100644 (file)
@@ -68,10 +68,49 @@ public class SeqCigar extends CigarSimple
   }
 
   /**
+   * 
+   * @param column
+   * @return position in sequence for column (or -1 if no match state exists)
+   */
+  public int findPosition(int column)
+  {
+    int w = 0, ew, p = refseq.findPosition(start);
+    if (column < 0)
+    {
+      return -1;
+    }
+    if (range != null)
+    {
+      for (int i = 0; i < length; i++)
+      {
+        if (operation[i] == M || operation[i] == D)
+        {
+          p += range[i];
+        }
+        if (operation[i] == M || operation[i] == I)
+        {
+          ew = w + range[i];
+          if (column < ew)
+          {
+            if (operation[i] == I)
+            {
+              return -1;
+            }
+            return p - (ew - column);
+          }
+          w = ew;
+        }
+      }
+    }
+    return -1;
+  }
+
+  /**
    * Returns sequence as a string with cigar operations applied to it
    * 
    * @return String
    */
+  @Override
   public String getSequenceString(char GapChar)
   {
     return (length == 0) ? "" : (String) getSequenceAndDeletions(
index 6c8cbc0..a61f093 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.api.DBRefEntryI;
 import jalview.util.StringUtils;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Vector;
@@ -185,12 +186,13 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   /**
-   * Creates a new Sequence object with new features, DBRefEntries,
-   * AlignmentAnnotations, and PDBIds but inherits any existing dataset sequence
-   * reference.
+   * Creates a new Sequence object with new AlignmentAnnotations but inherits
+   * any existing dataset sequence reference. If non exists, everything is
+   * copied.
    * 
    * @param seq
-   *          DOCUMENT ME!
+   *          if seq is a dataset sequence, behaves like a plain old copy
+   *          constructor
    */
   public Sequence(SequenceI seq)
   {
@@ -213,31 +215,48 @@ public class Sequence extends ASequence implements SequenceI
 
   }
 
+  /**
+   * does the heavy lifting when cloning a dataset sequence, or coping data from
+   * dataset to a new derived sequence.
+   * 
+   * @param seq
+   *          - source of attributes.
+   * @param alAnnotation
+   *          - alignment annotation present on seq that should be copied onto
+   *          this sequence
+   */
   protected void initSeqFrom(SequenceI seq,
           AlignmentAnnotation[] alAnnotation)
   {
-    initSeqAndName(seq.getName(), seq.getSequence(), seq.getStart(),
+    {
+      char[] oseq = seq.getSequence();
+      initSeqAndName(seq.getName(), Arrays.copyOf(oseq, oseq.length),
+              seq.getStart(),
             seq.getEnd());
+    }
     description = seq.getDescription();
     sourceDBRef = seq.getSourceDBRef() == null ? null : new DBRefEntry(
             seq.getSourceDBRef());
-    if (seq.getSequenceFeatures() != null)
+    if (seq != datasetSequence)
     {
-      SequenceFeature[] sf = seq.getSequenceFeatures();
-      for (int i = 0; i < sf.length; i++)
-      {
-        addSequenceFeature(new SequenceFeature(sf[i]));
-      }
+      setDatasetSequence(seq.getDatasetSequence());
     }
-    setDatasetSequence(seq.getDatasetSequence());
     if (datasetSequence == null && seq.getDBRefs() != null)
     {
-      // only copy DBRefs if we really are a dataset sequence
+      // only copy DBRefs and seqfeatures if we really are a dataset sequence
       DBRefEntry[] dbr = seq.getDBRefs();
       for (int i = 0; i < dbr.length; i++)
       {
         addDBRef(new DBRefEntry(dbr[i]));
       }
+      if (seq.getSequenceFeatures() != null)
+      {
+        SequenceFeature[] sf = seq.getSequenceFeatures();
+        for (int i = 0; i < sf.length; i++)
+        {
+          addSequenceFeature(new SequenceFeature(sf[i]));
+        }
+      }
     }
     if (seq.getAnnotation() != null)
     {
@@ -274,22 +293,30 @@ public class Sequence extends ASequence implements SequenceI
     }
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param v
-   *          DOCUMENT ME!
-   */
+
   @Override
   public void setSequenceFeatures(SequenceFeature[] features)
   {
-    sequenceFeatures = features;
+    if (datasetSequence == null)
+    {
+      sequenceFeatures = features;
+    }
+    else
+    {
+      System.err
+              .println("Warning: JAL-2046 side effect ? Possible implementation error: overwriting dataset sequence features by setting sequence features on alignment");
+      datasetSequence.setSequenceFeatures(features);
+    }
   }
 
   @Override
   public synchronized void addSequenceFeature(SequenceFeature sf)
   {
-    // TODO add to dataset sequence instead if there is one?
+    if (sequenceFeatures==null && datasetSequence != null)
+    {
+      datasetSequence.addSequenceFeature(sf);
+      return;
+    }
     if (sequenceFeatures == null)
     {
       sequenceFeatures = new SequenceFeature[0];
@@ -315,6 +342,9 @@ public class Sequence extends ASequence implements SequenceI
   {
     if (sequenceFeatures == null)
     {
+      if (datasetSequence!=null) {
+         datasetSequence.deleteFeature(sf);
+      }
       return;
     }
 
@@ -1037,31 +1067,24 @@ public class Sequence extends ASequence implements SequenceI
   @Override
   public SequenceI deriveSequence()
   {
-    SequenceI seq = new Sequence(this);
-    if (datasetSequence != null)
-    {
-      // duplicate current sequence with same dataset
-      seq.setDatasetSequence(datasetSequence);
-    }
-    else
+    Sequence seq=null;
+    if (datasetSequence == null)
     {
       if (isValidDatasetSequence())
       {
         // Use this as dataset sequence
+        seq = new Sequence(getName(), "", 1, -1);
         seq.setDatasetSequence(this);
+        seq.initSeqFrom(this, getAnnotation());
+        return seq;
       }
       else
       {
         // Create a new, valid dataset sequence
-        SequenceI ds = seq;
-        ds.setSequence(AlignSeq.extractGaps(
-                jalview.util.Comparison.GapChars, new String(sequence)));
-        setDatasetSequence(ds);
-        ds.setSequenceFeatures(getSequenceFeatures());
-        seq = this; // and return this sequence as the derived sequence.
+       createDatasetSequence();
       }
     }
-    return seq;
+    return new Sequence(this);
   }
 
   /*
@@ -1074,20 +1097,27 @@ public class Sequence extends ASequence implements SequenceI
   {
     if (datasetSequence == null)
     {
-      datasetSequence = new Sequence(getName(), AlignSeq.extractGaps(
+      Sequence dsseq = new Sequence(getName(), AlignSeq.extractGaps(
               jalview.util.Comparison.GapChars, getSequenceAsString()),
               getStart(), getEnd());
-      datasetSequence.setSequenceFeatures(getSequenceFeatures());
-      datasetSequence.setDescription(getDescription());
-      setSequenceFeatures(null);
-      // move database references onto dataset sequence
-      datasetSequence.setDBRefs(getDBRefs());
-      setDBRefs(null);
-      datasetSequence.setPDBId(getAllPDBEntries());
-      setPDBId(null);
+
+      datasetSequence = dsseq;
+
+      dsseq.setDescription(description);
+      // move features and database references onto dataset sequence
+      dsseq.sequenceFeatures = sequenceFeatures;
+      sequenceFeatures=null;
+      dsseq.dbrefs = dbrefs;
+      dbrefs=null;
+      // TODO: search and replace any references to this sequence with
+      // references to the dataset sequence in Mappings on dbref
+      dsseq.pdbIds = pdbIds;
+      pdbIds = null;
       datasetSequence.updatePDBIds();
       if (annotation != null)
       {
+        // annotation is cloned rather than moved, to preserve what's currently
+        // on the alignment
         for (AlignmentAnnotation aa : annotation)
         {
           AlignmentAnnotation _aa = new AlignmentAnnotation(aa);
index 4a7706f..f2eb8ac 100755 (executable)
@@ -39,6 +39,10 @@ public class SequenceFeature
   // private key for Phase designed not to conflict with real GFF data
   private static final String PHASE = "!Phase";
 
+  /*
+   * ATTRIBUTES is reserved for the GFF 'column 9' data, formatted as
+   * name1=value1;name2=value2,value3;...etc
+   */
   private static final String ATTRIBUTES = "ATTRIBUTES";
 
   public int begin;
@@ -111,26 +115,57 @@ public class SequenceFeature
     }
   }
 
+  /**
+   * Constructor including a Status value
+   * 
+   * @param type
+   * @param desc
+   * @param status
+   * @param begin
+   * @param end
+   * @param featureGroup
+   */
   public SequenceFeature(String type, String desc, String status,
           int begin, int end, String featureGroup)
   {
+    this(type, desc, begin, end, featureGroup);
+    setStatus(status);
+  }
+
+  /**
+   * Constructor
+   * 
+   * @param type
+   * @param desc
+   * @param begin
+   * @param end
+   * @param featureGroup
+   */
+  SequenceFeature(String type, String desc, int begin, int end,
+          String featureGroup)
+  {
     this.type = type;
     this.description = desc;
-    setValue(STATUS, status);
     this.begin = begin;
     this.end = end;
     this.featureGroup = featureGroup;
   }
 
+  /**
+   * Constructor including a score value
+   * 
+   * @param type
+   * @param desc
+   * @param begin
+   * @param end
+   * @param score
+   * @param featureGroup
+   */
   public SequenceFeature(String type, String desc, int begin, int end,
           float score, String featureGroup)
   {
-    this.type = type;
-    this.description = desc;
-    this.begin = begin;
-    this.end = end;
+    this(type, desc, begin, end, featureGroup);
     this.score = score;
-    this.featureGroup = featureGroup;
   }
 
   /**
index f1cba43..69eb1d4 100755 (executable)
@@ -250,17 +250,20 @@ public interface SequenceI extends ASequenceI
   public void insertCharAt(int position, int count, char ch);
 
   /**
-   * DOCUMENT ME!
+   * Gets array holding sequence features associated with this sequence. The
+   * array may be held by the sequence's dataset sequence if that is defined.
    * 
-   * @return DOCUMENT ME!
+   * @return hard reference to array
    */
   public SequenceFeature[] getSequenceFeatures();
 
   /**
-   * DOCUMENT ME!
+   * Replaces the array of sequence features associated with this sequence with
+   * a new array reference. If this sequence has a dataset sequence, then this
+   * method will update the dataset sequence's feature array
    * 
-   * @param v
-   *          DOCUMENT ME!
+   * @param features
+   *          New array of sequence features
    */
   public void setSequenceFeatures(SequenceFeature[] features);
 
@@ -432,7 +435,21 @@ public interface SequenceI extends ASequenceI
    */
   public PDBEntry getPDBEntry(String pdbId);
 
+  /**
+   * Set the distinct source database, and accession number from which a
+   * sequence and its start-end data were derived from. This is very important
+   * for SIFTS mappings and must be set prior to performing SIFTS mapping.
+   * 
+   * @param dbRef
+   *          the source dbRef for the sequence
+   */
   public void setSourceDBRef(DBRefEntryI dbRef);
 
+  /**
+   * Get the distinct source database, and accession number from which a
+   * sequence and its start-end data were derived from.
+   * 
+   * @return
+   */
   public DBRefEntryI getSourceDBRef();
 }
index 0bc6a74..4dd1bba 100644 (file)
@@ -11,7 +11,6 @@ import jalview.io.gff.SequenceOntologyI;
 import jalview.schemes.FeatureColourAdapter;
 import jalview.schemes.FeatureSettingsAdapter;
 import jalview.util.MapList;
-import jalview.util.StringUtils;
 
 import java.awt.Color;
 import java.io.UnsupportedEncodingException;
@@ -101,59 +100,91 @@ public class EnsemblGene extends EnsemblSeqProxy
    * </ul>
    * 
    * @param query
-   *          one or more identifiers separated by a space
-   * @return an alignment containing one or more genes, and possibly
-   *         transcripts, or null
+   *          a single gene or transcript identifier or gene name
+   * @return an alignment containing a gene, and possibly transcripts, or null
    */
   @Override
   public AlignmentI getSequenceRecords(String query) throws Exception
   {
-    // todo: tidy up handling of one or multiple accession ids
-    String[] queries = query.split(getAccessionSeparator());
-
     /*
-     * if given a transcript id, look up its gene parent
+     * convert to a non-duplicated list of gene identifiers
      */
-    if (isTranscriptIdentifier(query))
+    List<String> geneIds = getGeneIds(query);
+
+    AlignmentI al = null;
+    for (String geneId : geneIds)
     {
-      // we are assuming all transcripts have the same gene parent here
-      query = new EnsemblLookup(getDomain()).getParent(queries[0]);
-      if (query == null)
+      /*
+       * fetch the gene sequence(s) with features and xrefs
+       */
+      AlignmentI geneAlignment = super.getSequenceRecords(geneId);
+
+      if (geneAlignment.getHeight() == 1)
       {
-        return null;
+        getTranscripts(geneAlignment, geneId);
+      }
+      if (al == null)
+      {
+        al = geneAlignment;
+      }
+      else
+      {
+        al.append(geneAlignment);
       }
     }
+    return al;
+  }
 
-    /*
-     * if given a gene or other external name, lookup and fetch 
-     * the corresponding gene for all model organisms 
-     */
-    if (!isGeneIdentifier(query))
+  /**
+   * Converts a query, which may contain one or more gene or transcript
+   * identifiers, into a non-redundant list of gene identifiers.
+   * 
+   * @param accessions
+   * @return
+   */
+  List<String> getGeneIds(String accessions)
+  {
+    List<String> geneIds = new ArrayList<String>();
+
+    for (String acc : accessions.split(getAccessionSeparator()))
     {
-      List<String> geneIds = new EnsemblSymbol(getDomain()).getIds(query);
-      if (geneIds.isEmpty())
+      if (isGeneIdentifier(acc))
       {
-        return null;
+        if (!geneIds.contains(acc))
+        {
+          geneIds.add(acc);
+        }
       }
-      String theIds = StringUtils.listToDelimitedString(geneIds,
-              getAccessionSeparator());
-      return getSequenceRecords(theIds);
-    }
 
-    /*
-     * fetch the gene sequence(s) with features and xrefs
-     */
-    AlignmentI al = super.getSequenceRecords(query);
+      /*
+       * if given a transcript id, look up its gene parent
+       */
+      else if (isTranscriptIdentifier(acc))
+      {
+        String geneId = new EnsemblLookup(getDomain()).getParent(acc);
+        if (geneId != null && !geneIds.contains(geneId))
+        {
+          geneIds.add(geneId);
+        }
+      }
 
-    /*
-     * if we retrieved a single gene, get its transcripts as well
-     */
-    if (al.getHeight() == 1)
-    {
-      getTranscripts(al, query);
+      /*
+       * if given a gene or other external name, lookup and fetch 
+       * the corresponding gene for all model organisms 
+       */
+      else
+      {
+        List<String> ids = new EnsemblSymbol(getDomain()).getIds(acc);
+        for (String geneId : ids)
+        {
+          if (!geneIds.contains(geneId))
+          {
+            geneIds.add(geneId);
+          }
+        }
+      }
     }
-
-    return al;
+    return geneIds;
   }
 
   /**
diff --git a/src/jalview/ext/ensembl/EnsemblInfo.java b/src/jalview/ext/ensembl/EnsemblInfo.java
new file mode 100644 (file)
index 0000000..950b658
--- /dev/null
@@ -0,0 +1,71 @@
+package jalview.ext.ensembl;
+
+/**
+ * A data class to model the data and rest version of one Ensembl domain,
+ * currently for rest.ensembl.org and rest.ensemblgenomes.org
+ * 
+ * @author gmcarstairs
+ */
+class EnsemblInfo
+{
+  /*
+   * The http domain this object is holding data values for
+   */
+  String domain;
+
+  /*
+   * The latest version Jalview has tested for, e.g. "4.5"; a minor version change should be
+   * ok, a major version change may break stuff 
+   */
+  String expectedRestVersion;
+
+  /*
+   * Major / minor / point version e.g. "4.5.1"
+   * @see http://rest.ensembl.org/info/rest/?content-type=application/json
+   */
+  String restVersion;
+
+  /*
+   * data version
+   * @see http://rest.ensembl.org/info/data/?content-type=application/json
+   */
+  String dataVersion;
+
+  /*
+   * true when http://rest.ensembl.org/info/ping/?content-type=application/json
+   * returns response code 200
+   */
+  boolean restAvailable;
+
+  /*
+   * absolute time when availability was last checked
+   */
+  long lastAvailableCheckTime;
+
+  /*
+   * absolute time when version numbers were last checked
+   */
+  long lastVersionCheckTime;
+
+  // flag set to true if REST major version is not the one expected
+  boolean restMajorVersionMismatch;
+
+  /*
+   * absolute time to wait till if we overloaded the REST service
+   */
+  long retryAfter;
+
+  /**
+   * Constructor given expected REST version number e.g 4.5 or 3.4.3
+   * 
+   * @param restExpected
+   */
+  EnsemblInfo(String theDomain, String restExpected)
+  {
+    domain = theDomain;
+    expectedRestVersion = restExpected;
+    lastAvailableCheckTime = -1;
+    lastVersionCheckTime = -1;
+  }
+
+}
index 34f8816..e651ddf 100644 (file)
@@ -1,6 +1,7 @@
 package jalview.ext.ensembl;
 
 import jalview.io.FileParse;
+import jalview.util.StringUtils;
 
 import java.io.BufferedReader;
 import java.io.DataOutputStream;
@@ -10,10 +11,16 @@ import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.ws.rs.HttpMethod;
 
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+
 import com.stevesoft.pat.Regex;
 
 /**
@@ -23,14 +30,23 @@ import com.stevesoft.pat.Regex;
  */
 abstract class EnsemblRestClient extends EnsemblSequenceFetcher
 {
-  private final static String ENSEMBL_REST = "http://rest.ensembl.org";
+  /*
+   * update these constants when Jalview has been checked / updated for
+   * changes to Ensembl REST API
+   * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log
+   */
+  private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "4.4";
 
-  protected final static String ENSEMBL_GENOMES_REST = "http://rest.ensemblgenomes.org";
+  private static final String LATEST_ENSEMBL_REST_VERSION = "4.5";
+
+  private static Map<String, EnsemblInfo> domainData;
 
   // @see https://github.com/Ensembl/ensembl-rest/wiki/Output-formats
   private static final String PING_URL = "http://rest.ensembl.org/info/ping.json";
 
-  private final static long RETEST_INTERVAL = 10000L; // 10 seconds
+  private final static long AVAILABILITY_RETEST_INTERVAL = 10000L; // 10 seconds
+
+  private final static long VERSION_RETEST_INTERVAL = 1000L * 3600; // 1 hr
 
   private static final Regex TRANSCRIPT_REGEX = new Regex(
             "(ENS)([A-Z]{3}|)T[0-9]{11}$");
@@ -38,16 +54,14 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
   private static final Regex GENE_REGEX = new Regex(
             "(ENS)([A-Z]{3}|)G[0-9]{11}$");
 
-  private String domain = ENSEMBL_REST;
-
-  private static boolean ensemblRestAvailable = false;
-
-  private static long lastCheck = -1;
-
-  /*
-   * absolute time to wait till if we overloaded the REST service
-   */
-  private static long retryAfter;
+  static
+  {
+    domainData = new HashMap<String, EnsemblInfo>();
+    domainData.put(ENSEMBL_REST, new EnsemblInfo(ENSEMBL_REST,
+            LATEST_ENSEMBL_REST_VERSION));
+    domainData.put(ENSEMBL_GENOMES_REST, new EnsemblInfo(
+            ENSEMBL_GENOMES_REST, LATEST_ENSEMBLGENOMES_REST_VERSION));
+  }
 
   protected volatile boolean inProgress = false;
 
@@ -66,24 +80,28 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
    */
   public EnsemblRestClient(String d)
   {
-    domain = d;
-  }
-
-  String getDomain()
-  {
-    return domain;
-  }
-
-  void setDomain(String d)
-  {
-    domain = d;
+    setDomain(d);
   }
 
+  /**
+   * Answers true if the query matches the regular expression pattern for an
+   * Ensembl transcript stable identifier
+   * 
+   * @param query
+   * @return
+   */
   public boolean isTranscriptIdentifier(String query)
   {
     return query == null ? false : TRANSCRIPT_REGEX.search(query);
   }
 
+  /**
+   * Answers true if the query matches the regular expression pattern for an
+   * Ensembl gene stable identifier
+   * 
+   * @param query
+   * @return
+   */
   public boolean isGeneIdentifier(String query)
   {
     return query == null ? false : GENE_REGEX.search(query);
@@ -202,7 +220,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
      * POST method allows multiple queries in one request; it is supported for
      * sequence queries, but not for overlap
      */
-    boolean multipleIds = ids.size() > 1;// useGetRequest();
+    boolean multipleIds = ids != null && ids.size() > 1;
     connection.setRequestMethod(multipleIds ? HttpMethod.POST
             : HttpMethod.GET);
     connection.setRequestProperty("Content-Type",
@@ -264,13 +282,14 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
     // to test:
     // retryDelay = "5";
 
+    EnsemblInfo info = domainData.get(getDomain());
     if (retryDelay != null)
     {
       System.err.println("Ensembl REST service rate limit exceeded, wait "
               + retryDelay + " seconds before retrying");
       try
       {
-        retryAfter = System.currentTimeMillis()
+        info.retryAfter = System.currentTimeMillis()
                 + (1000 * Integer.valueOf(retryDelay));
       } catch (NumberFormatException e)
       {
@@ -280,46 +299,64 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
     }
     else
     {
-      retryAfter = 0;
+      info.retryAfter = 0;
       // debug:
       // System.out.println(String.format(
       // "%s Ensembl requests remaining of %s (reset in %ss)",
       // remaining, limit, reset));
     }
   }
+  
   /**
    * Rechecks if Ensembl is responding, unless the last check was successful and
    * the retest interval has not yet elapsed. Returns true if Ensembl is up,
-   * else false.
+   * else false. Also retrieves and saves the current version of Ensembl data
+   * and REST services at intervals.
    * 
    * @return
    */
   protected boolean isEnsemblAvailable()
   {
+    EnsemblInfo info = domainData.get(getDomain());
+
     long now = System.currentTimeMillis();
 
     /*
      * check if we are waiting for 'Retry-After' to expire
      */
-    if (retryAfter > now)
+    if (info.retryAfter > now)
     {
-      System.err.println("Still " + (1 + (retryAfter - now) / 1000)
+      System.err.println("Still " + (1 + (info.retryAfter - now) / 1000)
               + " secs to wait before retrying Ensembl");
       return false;
     }
     else
     {
-      retryAfter = 0;
+      info.retryAfter = 0;
     }
 
-    boolean retest = now - lastCheck > RETEST_INTERVAL;
-    if (ensemblRestAvailable && !retest)
+    /*
+     * recheck if Ensembl is up if it was down, or the recheck period has elapsed
+     */
+    boolean retestAvailability = (now - info.lastAvailableCheckTime) > AVAILABILITY_RETEST_INTERVAL;
+    if (!info.restAvailable || retestAvailability)
     {
-      return true;
+      info.restAvailable = checkEnsembl();
+      info.lastAvailableCheckTime = now;
     }
-    ensemblRestAvailable = checkEnsembl();
-    lastCheck = now;
-    return ensemblRestAvailable;
+
+    /*
+     * refetch Ensembl versions if the recheck period has elapsed
+     */
+    boolean refetchVersion = (now - info.lastVersionCheckTime) > VERSION_RETEST_INTERVAL;
+    if (refetchVersion)
+    {
+      checkEnsemblRestVersion();
+      checkEnsemblDataVersion();
+      info.lastVersionCheckTime = now;
+    }
+
+    return info.restAvailable;
   }
 
   /**
@@ -358,4 +395,104 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
     wr.close();
   }
 
+  /**
+   * Fetches and checks Ensembl's REST version number
+   * 
+   * @return
+   */
+  private void checkEnsemblRestVersion()
+  {
+    EnsemblInfo info = domainData.get(getDomain());
+
+    JSONParser jp = new JSONParser();
+    URL url = null;
+    try
+    {
+      url = new URL(getDomain()
+              + "/info/rest?content-type=application/json");
+      BufferedReader br = getHttpResponse(url, null);
+      JSONObject val = (JSONObject) jp.parse(br);
+      String version = val.get("release").toString();
+      String majorVersion = version.substring(0, version.indexOf("."));
+      String expected = info.expectedRestVersion;
+      String expectedMajorVersion = expected.substring(0,
+              expected.indexOf("."));
+      info.restMajorVersionMismatch = false;
+      try
+      {
+        /*
+         * if actual REST major version is ahead of what we expect,
+         * record this in case we want to warn the user
+         */
+        if (Float.valueOf(majorVersion) > Float
+                .valueOf(expectedMajorVersion))
+        {
+          info.restMajorVersionMismatch = true;
+        }
+      } catch (NumberFormatException e)
+      {
+        System.err.println("Error in REST version: " + e.toString());
+      }
+
+      /*
+       * check if REST version is later than what Jalview has tested against,
+       * if so warn; we don't worry if it is earlier (this indicates Jalview has
+       * been tested in advance against the next pending REST version)
+       */
+      boolean laterVersion = StringUtils.compareVersions(version, expected) == 1;
+      if (laterVersion)
+      {
+        System.err.println(String.format(
+                "Expected %s REST version %s but found %s", getDbSource(),
+                expected,
+                version));
+      }
+      info.restVersion = version;
+    } catch (Throwable t)
+    {
+      System.err.println("Error checking Ensembl REST version: "
+              + t.getMessage());
+    }
+  }
+
+  public boolean isRestMajorVersionMismatch()
+  {
+    return domainData.get(getDomain()).restMajorVersionMismatch;
+  }
+
+  /**
+   * Fetches and checks Ensembl's data version number
+   * 
+   * @return
+   */
+  private void checkEnsemblDataVersion()
+  {
+    JSONParser jp = new JSONParser();
+    URL url = null;
+    try
+    {
+      url = new URL(getDomain()
+              + "/info/data?content-type=application/json");
+      BufferedReader br = getHttpResponse(url, null);
+      JSONObject val = (JSONObject) jp.parse(br);
+      JSONArray versions = (JSONArray) val.get("releases");
+      domainData.get(getDomain()).dataVersion = versions.get(0).toString();
+    } catch (Throwable t)
+    {
+      System.err.println("Error checking Ensembl data version: "
+              + t.getMessage());
+    }
+  }
+
+  public String getEnsemblDataVersion()
+  {
+    return domainData.get(getDomain()).dataVersion;
+  }
+
+  @Override
+  public String getDbVersion()
+  {
+    return getEnsemblDataVersion();
+  }
+
 }
index 0ceb29c..c86469f 100644 (file)
@@ -14,6 +14,7 @@ import jalview.io.FastaFile;
 import jalview.io.FileParse;
 import jalview.io.gff.SequenceOntologyFactory;
 import jalview.io.gff.SequenceOntologyI;
+import jalview.util.Comparison;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 
@@ -36,8 +37,6 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
 {
   private static final String ALLELES = "alleles";
 
-  protected static final String CONSEQUENCE_TYPE = "consequence_type";
-
   protected static final String PARENT = "Parent";
 
   protected static final String ID = "ID";
@@ -57,7 +56,7 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
     GENOMIC("genomic"),
 
     /**
-     * type=cdna to fetch dna including UTRs
+     * type=cdna to fetch coding dna including UTRs
      */
     CDNA("cdna"),
 
@@ -281,8 +280,8 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
         ds.setSourceDBRef(proteinSeq.getSourceDBRef());
 
         Mapping map = new Mapping(ds, mapList);
-        DBRefEntry dbr = new DBRefEntry(getDbSource(), getDbVersion(),
-                proteinSeq.getName(), map);
+        DBRefEntry dbr = new DBRefEntry(getDbSource(),
+                getEnsemblDataVersion(), proteinSeq.getName(), map);
         querySeq.getDatasetSequence().addDBRef(dbr);
         
         /*
@@ -328,7 +327,8 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
     /*
      * and add a reference to itself
      */
-    DBRefEntry self = new DBRefEntry(getDbSource(), "0", seq.getName());
+    DBRefEntry self = new DBRefEntry(getDbSource(),
+            getEnsemblDataVersion(), seq.getName());
     seq.addDBRef(self);
   }
 
@@ -387,7 +387,8 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
         if (ids.contains(name)
                 || ids.contains(name.replace("ENSP", "ENST")))
         {
-          DBRefUtils.parseToDbRef(sq, DBRefSource.ENSEMBL, "0", name);
+          DBRefUtils.parseToDbRef(sq, getDbSource(),
+                  getEnsemblDataVersion(), name);
         }
       }
       if (alignment == null)
@@ -681,16 +682,21 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient
     {
       complement.append(",");
     }
-    if ("HGMD_MUTATION".equalsIgnoreCase(allele))
+
+    /*
+     * some 'alleles' are actually descriptive terms 
+     * e.g. HGMD_MUTATION, PhenCode_variation
+     * - we don't want to 'reverse complement' these
+     */
+    if (!Comparison.isNucleotideSequence(allele, true))
     {
       complement.append(allele);
     }
     else
     {
-      char[] alleles = allele.toCharArray();
-      for (int i = alleles.length - 1; i >= 0; i--)
+      for (int i = allele.length() - 1; i >= 0; i--)
       {
-        complement.append(Dna.getComplement(alleles[i]));
+        complement.append(Dna.getComplement(allele.charAt(i)));
       }
     }
   }
index 9a4952e..dd1739b 100644 (file)
@@ -20,6 +20,10 @@ abstract class EnsemblSequenceFetcher extends DbSourceProxyImpl
   private static final Regex ACCESSION_REGEX = new Regex(
           "(ENS([A-Z]{3}|)[GTEP]{1}[0-9]{11}$)" + "|" + "(CCDS[0-9.]{3,}$)");
 
+  protected static final String ENSEMBL_GENOMES_REST = "http://rest.ensemblgenomes.org";
+
+  protected static final String ENSEMBL_REST = "http://rest.ensembl.org";
+
   /*
    * possible values for the 'feature' parameter of the /overlap REST service
    * @see http://rest.ensembl.org/documentation/info/overlap_id
@@ -31,17 +35,17 @@ abstract class EnsemblSequenceFetcher extends DbSourceProxyImpl
     constrained, regulatory
   }
 
+  private String domain = ENSEMBL_REST;
+
   @Override
   public String getDbSource()
   {
     // NB ensure Uniprot xrefs are canonicalised from "Ensembl" to "ENSEMBL"
-    return DBRefSource.ENSEMBL; // "ENSEMBL"
-  }
-
-  @Override
-  public String getDbVersion()
-  {
-    return "0";
+    if (ENSEMBL_GENOMES_REST.equals(getDomain()))
+    {
+      return DBRefSource.ENSEMBLGENOMES;
+    }
+    return DBRefSource.ENSEMBL;
   }
 
   @Override
@@ -90,4 +94,20 @@ abstract class EnsemblSequenceFetcher extends DbSourceProxyImpl
   {
     return true;
   }
+
+  /**
+   * Returns the domain name to query e.g. http://rest.ensembl.org or
+   * http://rest.ensemblgenomes.org
+   * 
+   * @return
+   */
+  protected String getDomain()
+  {
+    return domain;
+  }
+
+  protected void setDomain(String d)
+  {
+    domain = d;
+  }
 }
diff --git a/src/jalview/fts/api/FTSData.java b/src/jalview/fts/api/FTSData.java
new file mode 100644 (file)
index 0000000..9e9d62c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package jalview.fts.api;
+
+/**
+ * This interface provides a model for the summary data;
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public interface FTSData
+{
+
+  /**
+   * Return an array of Objects representing the retrieved FTS data
+   * 
+   * @return
+   */
+  public Object[] getSummaryData();
+
+  /**
+   * The primary key object for the retrieved FTS data
+   * 
+   * @return
+   */
+  public Object getPrimaryKey();
+}
diff --git a/src/jalview/fts/api/FTSDataColumnI.java b/src/jalview/fts/api/FTSDataColumnI.java
new file mode 100644 (file)
index 0000000..80990b4
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+package jalview.fts.api;
+
+/**
+ * This interface provides a model for the dynamic data column configuration
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public interface FTSDataColumnI
+{
+  /**
+   * Returns the name of the data column
+   * 
+   * @return the data column's name
+   */
+  public String getName();
+
+  /**
+   * Returns the code of the data column
+   * 
+   * @return the data column's code
+   */
+  public String getCode();
+
+  /**
+   * Returns the alternative code value for the data column
+   * 
+   * @return the data column's code
+   */
+  public String getAltCode();
+
+  /**
+   * Returns the minimum width of the data column
+   * 
+   * @return the data column's minimum width
+   */
+  public int getMinWidth();
+
+  /**
+   * Returns the maximum width of the data column
+   * 
+   * @return the data column's maximum width
+   */
+  public int getMaxWidth();
+
+  /**
+   * Returns the preferred width of the data column
+   * 
+   * @return the data column's preferred width
+   */
+  public int getPreferredWidth();
+
+  /**
+   * Determines if the data column is the primary key column
+   * 
+   * @return true if data column is the primary key column, otherwise false
+   */
+  public boolean isPrimaryKeyColumn();
+
+  /**
+   * Checks if the data column field can be used to perform a search query
+   * 
+   * @return true means the data column is searchable
+   */
+  public boolean isSearchable();
+
+  /**
+   * Checks if the data column is displayed by default
+   * 
+   * @return true means the data column is shown by default
+   */
+  public boolean isVisibleByDefault();
+
+  /**
+   * Returns the data column's FTS data column group
+   * 
+   * @return the FTSDataColumnGroupI for the column
+   */
+  public FTSDataColumnGroupI getGroup();
+
+  /**
+   * Returns the data columns data type POJO
+   * 
+   * @return the DataTypeI for the column
+   */
+  public DataTypeI getDataType();
+
+  /**
+   * This interface provides a model for the dynamic data column group
+   * 
+   */
+  public interface FTSDataColumnGroupI
+  {
+    /**
+     * Returns the Id of the data column's group
+     * 
+     * @return the data column's group Id
+     */
+    public String getID();
+
+    /**
+     * Returns the name of the group
+     * 
+     * @return the group's name
+     */
+    public String getName();
+
+    /**
+     * Returns the sort order of the group
+     * 
+     * @return the group's sort order
+     */
+    public int getSortOrder();
+  }
+
+  public interface DataTypeI
+  {
+    /**
+     * Returns the data column's data type class
+     * 
+     * @return the Class for the data column's data type
+     */
+    public Class getDataTypeClass();
+
+    /**
+     * Checks if the numeric data column's data will be formated
+     * 
+     * @return true means the numeric data column shall be formatted
+     */
+    public boolean isFormtted();
+
+    /**
+     * Returns the number of significant figure to be used for the numeric value
+     * formatting
+     * 
+     * @return the number of significant figures
+     */
+    public int getSignificantFigures();
+  }
+}
diff --git a/src/jalview/fts/api/FTSRestClientI.java b/src/jalview/fts/api/FTSRestClientI.java
new file mode 100644 (file)
index 0000000..2266ca0
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+package jalview.fts.api;
+
+import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+
+import java.util.Collection;
+
+/**
+ * Methods for FTS Rest client.
+ * 
+ * @author tcnofoegbu
+ */
+public interface FTSRestClientI
+{
+
+  /**
+   * Execute a given FTS request, process the response and return it as an
+   * FTSRestResponse object
+   * 
+   * @param ftsRestRequest
+   *          the FTS request to execute
+   * @return FTSRestResponse - the response after executing an FTS request
+   * @throws Exception
+   */
+  public FTSRestResponse executeRequest(FTSRestRequest ftsRequest)
+          throws Exception;
+
+  /**
+   * Return the resource file path for the data columns configuration file
+   * 
+   * @return
+   */
+  public String getColumnDataConfigFileName();
+
+  /**
+   * Fetch FTSDataColumnGroupI by the group's Id
+   * 
+   * @param groupId
+   * @return FTSDataColumnGroupI
+   * @throws Exception
+   */
+  public FTSDataColumnGroupI getDataColumnGroupById(String groupId)
+          throws Exception;
+
+  /**
+   * Fetch FTSDataColumnI by name or code
+   * 
+   * @param nameOrCode
+   * @return FTSDataColumnI
+   * @throws Exception
+   */
+  public FTSDataColumnI getDataColumnByNameOrCode(String nameOrCode)
+          throws Exception;
+
+
+  /**
+   * Convert collection of FTSDataColumnI objects to a comma delimited string of
+   * the 'code' values
+   * 
+   * @param wantedFields
+   *          the collection of FTSDataColumnI to process
+   * @return the generated comma delimited string from the supplied
+   *         FTSDataColumnI collection
+   */
+  public String getDataColumnsFieldsAsCommaDelimitedString(
+          Collection<FTSDataColumnI> wantedFields);
+
+
+  /**
+   * Fetch index of the primary key column for the dynamic table
+   * 
+   * @param wantedFields
+   *          the available table columns
+   * @param hasRefSeq
+   *          true if the data columns has an additional column for reference
+   *          sequence
+   * @return index of the primary key column
+   * @throws Exception
+   */
+  public int getPrimaryKeyColumIndex(
+          Collection<FTSDataColumnI> wantedFields, boolean hasRefSeq)
+          throws Exception;
+          
+  /**
+   * Fetch the primary key data column object
+   * 
+   * @return the FTSDataColumnI object for the primary key column
+   */
+  public FTSDataColumnI getPrimaryKeyColumn();
+
+  /**
+   * Returns list of FTSDataColumnI objects to be displayed by default
+   * 
+   * @return list of columns to display by default
+   */
+  public Collection<FTSDataColumnI> getAllDefaulDisplayedDataColumns();
+
+  /**
+   * Return list of FTSDataColumnI objects that can be used to perform a search
+   * query
+   * 
+   * @return list of searchable FTSDataColumnI object
+   */
+  public Collection<FTSDataColumnI> getSearchableDataColumns();
+
+  /**
+   * Return list of all available FTSDataColumnI object
+   * 
+   * @return list of all FTSColumnI objcet
+   */
+  public Collection<FTSDataColumnI> getAllFTSDataColumns();
+
+  /**
+   * Return the default response page limit
+   * 
+   * @return the default response page size
+   */
+  public int getDefaultResponsePageSize();
+}
+
diff --git a/src/jalview/fts/api/GFTSPanelI.java b/src/jalview/fts/api/GFTSPanelI.java
new file mode 100644 (file)
index 0000000..ce63576
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+package jalview.fts.api;
+
+import javax.swing.JTable;
+
+/**
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public interface GFTSPanelI
+{
+
+  /**
+   * Action performed when a text is entered in the search field.
+   * 
+   * @param isFreshSearch
+   *          if true a fresh search is executed else a pagination search is
+   *          executed
+   */
+  public void searchAction(boolean isFreshSearch);
+
+  /**
+   * Action performed when search results are selected and the 'ok' button is
+   * pressed.
+   */
+  public void okAction();
+
+  /**
+   * Return the entered text
+   * 
+   * @return the entered text
+   */
+  public String getTypedText();
+
+  /**
+   * The JTable for presenting the query result
+   * 
+   * @return JTable
+   */
+  public JTable getResultTable();
+
+  /**
+   * Return the title to display on the search interface main panel
+   * 
+   * @return String - the title
+   */
+  public String getFTSFrameTitle();
+
+  /**
+   * Return a singleton instance of FTSRestClientI
+   * 
+   * @return FTSRestClientI
+   */
+  public FTSRestClientI getFTSRestClient();
+
+  /**
+   * Set error message when one occurs
+   * 
+   * @param message
+   *          the error message to set
+   */
+  public void setErrorMessage(String message);
+
+  /**
+   * Updates the title displayed on the search interface's main panel
+   * 
+   * @param newTitle
+   */
+  public void updateSearchFrameTitle(String newTitle);
+
+  /**
+   * Controls the progress spinner, set to 'true' while search operation is in
+   * progress and 'false' after it completes
+   * 
+   * @param isSearchInProgress
+   */
+  public void setSearchInProgress(Boolean isSearchInProgress);
+
+  /**
+   * Action performed when previous page (<<) button is pressed pressed.
+   */
+  public void prevPageAction();
+
+  /**
+   * Action performed when next page (>>) button is pressed pressed.
+   */
+  public void nextPageAction();
+
+  /**
+   * Checks if the current service's search result is paginate-able
+   * 
+   * @return true means the service provides paginated results
+   */
+  public boolean isPaginationEnabled();
+
+  /**
+   * Updates the 'enabled' state for the previous page button
+   * 
+   * @param isEnabled
+   */
+  public void setPrevPageButtonEnabled(boolean isEnabled);
+
+  /**
+   * Updates the 'enabled' state for the next page button
+   * 
+   * @param isEnabled
+   */
+  public void setNextPageButtonEnabled(boolean isEnabled);
+}
diff --git a/src/jalview/fts/core/DecimalFormatTableCellRenderer.java b/src/jalview/fts/core/DecimalFormatTableCellRenderer.java
new file mode 100644 (file)
index 0000000..3642721
--- /dev/null
@@ -0,0 +1,60 @@
+package jalview.fts.core;
+
+import java.awt.Component;
+import java.text.DecimalFormat;
+
+import javax.swing.JLabel;
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableCellRenderer;
+
+/**
+ * The class to handle the formatting of the double values for JTable cells.
+ */
+public class DecimalFormatTableCellRenderer extends
+        DefaultTableCellRenderer
+{
+  private DecimalFormat formatter;
+
+  public DecimalFormatTableCellRenderer(boolean isFormated,
+          int significantFigures)
+  {
+    String integerFormater = isFormated ? "###,##0" : "0";
+    String fractionFormater = isFormated ? "###,##0." : "0.";
+    if (significantFigures > 0)
+    {
+      StringBuilder significantFigureBuilder = new StringBuilder();
+      for (int x = 1; x <= significantFigures; ++x)
+      {
+        significantFigureBuilder.append("0");
+      }
+      formatter = new DecimalFormat(fractionFormater
+              + significantFigureBuilder.toString());
+    }
+    else
+    {
+      formatter = new DecimalFormat(integerFormater);
+    }
+    super.setHorizontalAlignment(JLabel.RIGHT);
+  }
+
+  public DecimalFormatTableCellRenderer()
+  {
+    super.setHorizontalAlignment(JLabel.RIGHT);
+  }
+
+  @Override
+  public Component getTableCellRendererComponent(JTable table,
+          Object value, boolean isSelected, boolean hasFocus, int row,
+          int column)
+  {
+    if (value == null)
+    {
+      return null;
+    }
+
+    value = formatter.format(value);
+
+    return super.getTableCellRendererComponent(table, value, isSelected,
+            hasFocus, row, column);
+  }
+}
\ No newline at end of file
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.jbgui;
+package jalview.fts.core;
 
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField.Group;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+import jalview.fts.api.FTSRestClientI;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -40,18 +41,18 @@ import javax.swing.table.TableRowSorter;
 
 
 @SuppressWarnings("serial")
-public class PDBDocFieldPreferences extends JScrollPane
+public class FTSDataColumnPreferences extends JScrollPane
 {
-  protected JTable tbl_pdbDocFieldConfig = new JTable();
+  protected JTable tbl_FTSDataColumnPrefs = new JTable();
 
   protected JScrollPane scrl_pdbDocFieldConfig = new JScrollPane(
-          tbl_pdbDocFieldConfig);
+          tbl_FTSDataColumnPrefs);
 
-  private HashMap<String, PDBDocField> map = new HashMap<String, PDBDocField>();
+  private HashMap<String, FTSDataColumnI> map = new HashMap<String, FTSDataColumnI>();
 
-  private static Collection<PDBDocField> searchSummaryFields = new LinkedHashSet<PDBDocField>();
+  private Collection<FTSDataColumnI> structSummaryColumns = new LinkedHashSet<FTSDataColumnI>();
 
-  private static Collection<PDBDocField> structureSummaryFields = new LinkedHashSet<PDBDocField>();
+  private Collection<FTSDataColumnI> allFTSDataColumns = new LinkedHashSet<FTSDataColumnI>();
 
   public enum PreferenceSource
   {
@@ -60,24 +61,21 @@ public class PDBDocFieldPreferences extends JScrollPane
 
   private PreferenceSource currentSource;
 
-  static
-  {
-    searchSummaryFields.add(PDBDocField.PDB_ID);
-    searchSummaryFields.add(PDBDocField.TITLE);
-    searchSummaryFields.add(PDBDocField.RESOLUTION);
-
-    structureSummaryFields.add(PDBDocField.PDB_ID);
-    structureSummaryFields.add(PDBDocField.TITLE);
-    structureSummaryFields.add(PDBDocField.RESOLUTION);
-  }
+  private FTSRestClientI ftsRestClient;
 
-  public PDBDocFieldPreferences(PreferenceSource source)
+  public FTSDataColumnPreferences(PreferenceSource source,
+          FTSRestClientI ftsRestClient)
   {
-    tbl_pdbDocFieldConfig.setAutoCreateRowSorter(true);
+    this.ftsRestClient = ftsRestClient;
+    Collection<FTSDataColumnI> defaultCols = ftsRestClient
+            .getAllDefaulDisplayedDataColumns();
 
+    structSummaryColumns.addAll(defaultCols);
 
+    allFTSDataColumns.addAll(ftsRestClient.getAllFTSDataColumns());
 
-    this.getViewport().add(tbl_pdbDocFieldConfig);
+    tbl_FTSDataColumnPrefs.setAutoCreateRowSorter(true);
+    this.getViewport().add(tbl_FTSDataColumnPrefs);
     this.currentSource = source;
 
     String[] columnNames = null;
@@ -97,9 +95,10 @@ public class PDBDocFieldPreferences extends JScrollPane
       break;
     }
 
-    Object[][] data = new Object[PDBDocField.values().length - 1][3];
+    Object[][] data = new Object[allFTSDataColumns.size() - 1][3];
+
     int x = 0;
-    for (PDBDocField field : PDBDocField.values())
+    for (FTSDataColumnI field : allFTSDataColumns)
     {
       if (field.getName().equalsIgnoreCase("all"))
       {
@@ -109,17 +108,20 @@ public class PDBDocFieldPreferences extends JScrollPane
       switch (source)
       {
       case SEARCH_SUMMARY:
-        data[x++] = new Object[] { searchSummaryFields.contains(field),
+        data[x++] = new Object[] {
+            ftsRestClient.getAllDefaulDisplayedDataColumns()
+                    .contains(field),
             field.getName(), field.getGroup() };
         break;
       case STRUCTURE_CHOOSER:
-        data[x++] = new Object[] { structureSummaryFields.contains(field),
+        data[x++] = new Object[] { structSummaryColumns.contains(field),
             field.getName(), field.getGroup() };
         break;
       case PREFERENCES:
         data[x++] = new Object[] { field.getName(),
-            searchSummaryFields.contains(field),
-            structureSummaryFields.contains(field) };
+            ftsRestClient.getAllDefaulDisplayedDataColumns()
+                    .contains(field),
+            structSummaryColumns.contains(field) };
         break;
       default:
         break;
@@ -127,28 +129,28 @@ public class PDBDocFieldPreferences extends JScrollPane
       map.put(field.getName(), field);
     }
 
-    PDBFieldTableModel model = new PDBFieldTableModel(columnNames, data);
-    tbl_pdbDocFieldConfig.setModel(model);
+    FTSDataColumnPrefsTableModel model = new FTSDataColumnPrefsTableModel(columnNames, data);
+    tbl_FTSDataColumnPrefs.setModel(model);
 
     switch (source)
     {
     case SEARCH_SUMMARY:
     case STRUCTURE_CHOOSER:
-      tbl_pdbDocFieldConfig.getColumnModel().getColumn(0)
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(0)
               .setPreferredWidth(30);
-      tbl_pdbDocFieldConfig.getColumnModel().getColumn(0).setMinWidth(20);
-      tbl_pdbDocFieldConfig.getColumnModel().getColumn(0).setMaxWidth(40);
-      tbl_pdbDocFieldConfig.getColumnModel().getColumn(1)
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(0).setMinWidth(20);
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(0).setMaxWidth(40);
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(1)
               .setPreferredWidth(150);
-      tbl_pdbDocFieldConfig.getColumnModel().getColumn(1).setMinWidth(150);
-      tbl_pdbDocFieldConfig.getColumnModel().getColumn(2)
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(1).setMinWidth(150);
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(2)
               .setPreferredWidth(150);
-      tbl_pdbDocFieldConfig.getColumnModel().getColumn(2)
+      tbl_FTSDataColumnPrefs.getColumnModel().getColumn(2)
 .setMinWidth(150);
 
       TableRowSorter<TableModel> sorter = new TableRowSorter<>(
-              tbl_pdbDocFieldConfig.getModel());
-      tbl_pdbDocFieldConfig.setRowSorter(sorter);
+              tbl_FTSDataColumnPrefs.getModel());
+      tbl_FTSDataColumnPrefs.setRowSorter(sorter);
       List<RowSorter.SortKey> sortKeys = new ArrayList<>();
       int columnIndexToSort = 2;
       sortKeys.add(new RowSorter.SortKey(columnIndexToSort,
@@ -156,17 +158,18 @@ public class PDBDocFieldPreferences extends JScrollPane
       sorter.setSortKeys(sortKeys);
       sorter.setComparator(
               columnIndexToSort,
-              new Comparator<jalview.ws.dbsources.PDBRestClient.PDBDocField.Group>()
+              new Comparator<FTSDataColumnGroupI>()
               {
                 @Override
-                public int compare(Group o1, Group o2)
+                public int compare(FTSDataColumnGroupI o1,
+                        FTSDataColumnGroupI o2)
                 {
                   return o1.getSortOrder() - o2.getSortOrder();
                 }
               });
       sorter.sort();
 
-      tbl_pdbDocFieldConfig
+      tbl_FTSDataColumnPrefs
               .setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
       break;
     case PREFERENCES:
@@ -176,32 +179,15 @@ public class PDBDocFieldPreferences extends JScrollPane
 
   }
 
-  public static Collection<PDBDocField> getSearchSummaryFields()
+  public Collection<FTSDataColumnI> getStructureSummaryFields()
   {
-    return searchSummaryFields;
+    return structSummaryColumns;
   }
 
-  public static void setSearchSummaryFields(
-          Collection<PDBDocField> searchSummaryFields)
+  class FTSDataColumnPrefsTableModel extends AbstractTableModel
   {
-    PDBDocFieldPreferences.searchSummaryFields = searchSummaryFields;
-  }
-
-  public static Collection<PDBDocField> getStructureSummaryFields()
-  {
-    return structureSummaryFields;
-  }
 
-  public static void setStructureSummaryFields(
-          Collection<PDBDocField> structureSummaryFields)
-  {
-    PDBDocFieldPreferences.structureSummaryFields = structureSummaryFields;
-  }
-
-  class PDBFieldTableModel extends AbstractTableModel
-  {
-
-    public PDBFieldTableModel(String[] columnNames, Object[][] data)
+    public FTSDataColumnPrefsTableModel(String[] columnNames, Object[][] data)
     {
       this.data = data;
       this.columnNames = columnNames;
@@ -261,9 +247,9 @@ public class PDBDocFieldPreferences extends JScrollPane
       {
       case SEARCH_SUMMARY:
       case STRUCTURE_CHOOSER:
-        return (col == 0) && !isPDBID(row, 1);
+        return (col == 0) && !isPrimaryKeyCell(row, 1);
       case PREFERENCES:
-        return (col == 1 || col == 2) && !isPDBID(row, 0);
+        return (col == 1 || col == 2) && !isPrimaryKeyCell(row, 0);
       default:
         return false;
       }
@@ -278,16 +264,11 @@ public class PDBDocFieldPreferences extends JScrollPane
      * @return
      */
 
-    public boolean isPDBID(int row, int col)
+    public boolean isPrimaryKeyCell(int row, int col)
     {
-      boolean matched = false;
       String name = getValueAt(row, col).toString();
-      PDBDocField pdbField = map.get(name);
-      if (pdbField == PDBDocField.PDB_ID)
-      {
-        matched = true;
-      }
-      return matched;
+      FTSDataColumnI pdbField = map.get(name);
+      return pdbField.isPrimaryKeyColumn();
     }
 
     /*
@@ -314,40 +295,45 @@ public class PDBDocFieldPreferences extends JScrollPane
       }
       boolean selected = ((Boolean) value).booleanValue();
 
-      PDBDocField pdbField = map.get(name);
+      FTSDataColumnI ftsDataColumn = map.get(name);
 
       if (currentSource == PreferenceSource.SEARCH_SUMMARY)
       {
-        updatePrefs(searchSummaryFields, pdbField, selected);
+        updatePrefs(ftsRestClient
+                .getAllDefaulDisplayedDataColumns(), ftsDataColumn,
+                selected);
       }
       else if (currentSource == PreferenceSource.STRUCTURE_CHOOSER)
       {
-        updatePrefs(structureSummaryFields, pdbField, selected);
+        updatePrefs(structSummaryColumns, ftsDataColumn, selected);
       }
       else if (currentSource == PreferenceSource.PREFERENCES)
       {
         if (col == 1)
         {
-          updatePrefs(searchSummaryFields, pdbField, selected);
+          updatePrefs(ftsRestClient
+                  .getAllDefaulDisplayedDataColumns(), ftsDataColumn,
+                  selected);
         }
         else if (col == 2)
         {
-          updatePrefs(structureSummaryFields, pdbField, selected);
+          updatePrefs(structSummaryColumns, ftsDataColumn, selected);
         }
       }
     }
 
-    private void updatePrefs(Collection<PDBDocField> prefConfig,
-            PDBDocField pdbField, boolean selected)
+    private void updatePrefs(
+            Collection<FTSDataColumnI> prefConfig,
+            FTSDataColumnI dataColumn, boolean selected)
     {
-      if (prefConfig.contains(pdbField) && !selected)
+      if (prefConfig.contains(dataColumn) && !selected)
       {
-        prefConfig.remove(pdbField);
+        prefConfig.remove(dataColumn);
       }
 
-      if (!prefConfig.contains(pdbField) && selected)
+      if (!prefConfig.contains(dataColumn) && selected)
       {
-        prefConfig.add(pdbField);
+        prefConfig.add(dataColumn);
       }
     }
 
diff --git a/src/jalview/fts/core/FTSRestClient.java b/src/jalview/fts/core/FTSRestClient.java
new file mode 100644 (file)
index 0000000..f2df461
--- /dev/null
@@ -0,0 +1,489 @@
+package jalview.fts.core;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.util.MessageManager;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * Base class providing implementation for common methods defined in
+ * FTSRestClientI
+ * 
+ * @author tcnofoegbu
+ * 
+ * @note implementations MUST be accessed as a singleton.
+ */
+public abstract class FTSRestClient implements FTSRestClientI
+{
+  protected Collection<FTSDataColumnI> dataColumns = new ArrayList<FTSDataColumnI>();
+
+  protected Collection<FTSDataColumnGroupI> dataColumnGroups = new ArrayList<FTSDataColumnGroupI>();
+
+  protected Collection<FTSDataColumnI> searchableDataColumns = new ArrayList<FTSDataColumnI>();
+
+  protected Collection<FTSDataColumnI> defaulDisplayedDataColumns = new ArrayList<FTSDataColumnI>();
+
+  protected FTSDataColumnI primaryKeyColumn;
+
+  private String primaryKeyColumnCode = null;
+
+  private int defaultResponsePageSize = 100;
+
+  protected FTSRestClient()
+  {
+
+  }
+
+  public void parseDataColumnsConfigFile()
+  {
+    String fileName = getColumnDataConfigFileName();
+    
+    InputStream in = getClass().getResourceAsStream(fileName); 
+    
+    try (BufferedReader br = new BufferedReader(new InputStreamReader(in)))
+    {
+      String line;
+      while ((line = br.readLine()) != null)
+      {
+        final String[] lineData = line.split(";");
+        try
+        {
+          if (lineData.length == 2)
+          {
+            if (lineData[0].equalsIgnoreCase("_data_column.primary_key"))
+            {
+              primaryKeyColumnCode = lineData[1];
+            }
+            if (lineData[0]
+                    .equalsIgnoreCase("_data_column.default_response_page_size"))
+            {
+              defaultResponsePageSize = Integer.valueOf(lineData[1]);
+            }
+          }
+          else if (lineData.length == 3)
+          {
+            dataColumnGroups.add(new FTSDataColumnGroupI()
+            {
+              @Override
+              public String getID()
+              {
+                return lineData[0];
+              }
+
+              @Override
+              public String getName()
+              {
+                return lineData[1];
+              }
+
+              @Override
+              public int getSortOrder()
+              {
+                return Integer.valueOf(lineData[2]);
+              }
+
+              @Override
+              public String toString()
+              {
+                return lineData[1];
+              }
+
+              @Override
+              public int hashCode()
+              {
+                return Objects.hash(this.getID(), this.getName(),
+                        this.getSortOrder());
+              }
+
+              @Override
+              public boolean equals(Object otherObject)
+              {
+                FTSDataColumnGroupI that = (FTSDataColumnGroupI) otherObject;
+                return this.getID().equals(that.getID())
+                        && this.getName().equals(that.getName())
+                        && this.getSortOrder() == that.getSortOrder();
+              }
+            });
+          }
+          else if (lineData.length > 6)
+          {
+            FTSDataColumnI dataCol = new FTSDataColumnI()
+            {
+              @Override
+              public String toString()
+              {
+                return lineData[0];
+              }
+
+              @Override
+              public String getName()
+              {
+                return lineData[0];
+              }
+
+              @Override
+              public String getCode()
+              {
+                return lineData[1].split("\\|")[0];
+              }
+
+              @Override
+              public String getAltCode()
+              {
+                return lineData[1].split("\\|").length > 1 ? lineData[1]
+                        .split("\\|")[1] : getCode();
+              }
+
+              @Override
+              public DataTypeI getDataType()
+              {
+                final String[] dataTypeString = lineData[2].split("\\|");
+                final String classString = dataTypeString[0].toUpperCase();
+
+                return new DataTypeI()
+                {
+
+                  @Override
+                  public boolean isFormtted()
+                  {
+                    if (dataTypeString.length > 1
+                            && dataTypeString[1] != null)
+                    {
+                      switch (dataTypeString[1].toUpperCase())
+                      {
+                      case "T":
+                      case "TRUE":
+                        return true;
+                      case "F":
+                      case "False":
+                      default:
+                        return false;
+                      }
+                    }
+                    return false;
+                  }
+
+                  @Override
+                  public int getSignificantFigures()
+                  {
+                    if (dataTypeString.length > 2
+                            && dataTypeString[2] != null)
+                    {
+                      return Integer.valueOf(dataTypeString[2]);
+                    }
+                    return 0;
+                  }
+
+                  @Override
+                  public Class getDataTypeClass()
+                  {
+                    switch (classString)
+                    {
+                    case "INT":
+                    case "INTEGER":
+                      return Integer.class;
+                    case "DOUBLE":
+                      return Double.class;
+                    case "STRING":
+                    default:
+                      return String.class;
+                    }
+                  }
+                };
+
+              }
+
+              @Override
+              public FTSDataColumnGroupI getGroup()
+              {
+                FTSDataColumnGroupI group = null;
+                try
+                {
+                  group = getDataColumnGroupById(lineData[3]);
+                } catch (Exception e)
+                {
+                  e.printStackTrace();
+                }
+                return group;
+              }
+
+              @Override
+              public int getMinWidth()
+              {
+                return Integer.valueOf(lineData[4]);
+              }
+
+              @Override
+              public int getMaxWidth()
+              {
+                return Integer.valueOf(lineData[5]);
+              }
+
+              @Override
+              public int getPreferredWidth()
+              {
+                return Integer.valueOf(lineData[6]);
+              }
+
+              @Override
+              public boolean isPrimaryKeyColumn()
+              {
+                return getName().equalsIgnoreCase(primaryKeyColumnCode)
+                        || getCode().equalsIgnoreCase(primaryKeyColumnCode);
+              }
+
+              @Override
+              public boolean isVisibleByDefault()
+              {
+                return Boolean.valueOf(lineData[7]);
+              }
+
+              @Override
+              public boolean isSearchable()
+              {
+                return Boolean.valueOf(lineData[8]);
+              }
+
+              @Override
+              public int hashCode()
+              {
+                return Objects.hash(this.getName(), this.getCode(),
+                        this.getGroup());
+              }
+
+
+              @Override
+              public boolean equals(Object otherObject)
+              {
+                FTSDataColumnI that = (FTSDataColumnI) otherObject;
+                return this.getCode().equals(that.getCode())
+                        && this.getName().equals(that.getName())
+                        && this.getGroup().equals(that.getGroup());
+              }
+
+
+            };
+            dataColumns.add(dataCol);
+
+            if (dataCol.isSearchable())
+            {
+              searchableDataColumns.add(dataCol);
+            }
+
+            if (dataCol.isVisibleByDefault())
+            {
+              defaulDisplayedDataColumns.add(dataCol);
+            }
+
+          }
+          else
+          {
+            continue;
+          }
+        } catch (Exception e)
+        {
+          e.printStackTrace();
+        }
+      }
+      try
+      {
+        this.primaryKeyColumn = getDataColumnByNameOrCode(primaryKeyColumnCode);
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
+    } catch (IOException e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public int getPrimaryKeyColumIndex(
+          Collection<FTSDataColumnI> wantedFields, boolean hasRefSeq)
+          throws Exception
+  {
+
+    // If a reference sequence is attached then start counting from 1 else
+    // start from zero
+    int pdbFieldIndexCounter = hasRefSeq ? 1 : 0;
+
+    for (FTSDataColumnI field : wantedFields)
+    {
+      if (field.isPrimaryKeyColumn())
+      {
+        break; // Once PDB Id index is determined exit iteration
+      }
+      ++pdbFieldIndexCounter;
+    }
+    return pdbFieldIndexCounter;
+  }
+
+  @Override
+  public String getDataColumnsFieldsAsCommaDelimitedString(
+          Collection<FTSDataColumnI> dataColumnFields)
+  {
+    String result = "";
+    if (dataColumnFields != null && !dataColumnFields.isEmpty())
+    {
+      StringBuilder returnedFields = new StringBuilder();
+      for (FTSDataColumnI field : dataColumnFields)
+      {
+        returnedFields.append(",").append(field.getCode());
+      }
+      returnedFields.deleteCharAt(0);
+      result = returnedFields.toString();
+    }
+    return result;
+  }
+
+
+  @Override
+  public Collection<FTSDataColumnI> getAllFTSDataColumns()
+  {
+    if (dataColumns == null || dataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return dataColumns;
+  }
+
+  @Override
+  public Collection<FTSDataColumnI> getSearchableDataColumns()
+  {
+    if (searchableDataColumns == null || searchableDataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return searchableDataColumns;
+  }
+
+  @Override
+  public Collection<FTSDataColumnI> getAllDefaulDisplayedDataColumns()
+  {
+    if (defaulDisplayedDataColumns == null
+            || defaulDisplayedDataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return defaulDisplayedDataColumns;
+  }
+
+  @Override
+  public FTSDataColumnI getPrimaryKeyColumn()
+  {
+    if (defaulDisplayedDataColumns == null
+            || defaulDisplayedDataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return primaryKeyColumn;
+  }
+
+  @Override
+  public FTSDataColumnI getDataColumnByNameOrCode(String nameOrCode)
+          throws Exception
+  {
+    if (dataColumns == null || dataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    for (FTSDataColumnI column : dataColumns)
+    {
+      if (column.getName().equalsIgnoreCase(nameOrCode)
+              || column.getCode().equalsIgnoreCase(nameOrCode))
+      {
+        return column;
+      }
+    }
+    throw new Exception("Couldn't find data column with name : "
+            + nameOrCode);
+  }
+
+  @Override
+  public FTSDataColumnGroupI getDataColumnGroupById(String id)
+          throws Exception
+  {
+    if (dataColumns == null || dataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    for (FTSDataColumnGroupI columnGroup : dataColumnGroups)
+    {
+      if (columnGroup.getID().equalsIgnoreCase(id))
+      {
+        return columnGroup;
+      }
+    }
+    throw new Exception("Couldn't find data column group with id : " + id);
+  }
+
+  public String getMessageByHTTPStatusCode(int code, String service)
+  {
+    String message = "";
+    switch (code)
+    {
+    case 400:
+      message = MessageManager
+              .getString("exception.bad_request");
+      break;
+      
+    case 410:
+      message = MessageManager.formatMessage(
+              "exception.fts_rest_service_no_longer_available", service);
+      break;
+    case 403:
+    case 404:
+      message = MessageManager.getString("exception.resource_not_be_found");
+      break;
+    case 408:
+    case 409:
+    case 500:
+    case 501:
+    case 502:
+    case 504:
+    case 505:
+      message = MessageManager.getString("exception.fts_server_error");
+      break;
+    case 503:
+      message = MessageManager.getString("exception.service_not_available");
+      break;
+    default:
+      break;
+    }
+    return message;
+  }
+
+  protected String getResourceFile(String fileName)
+  {
+    String result = "";
+    try
+    {
+      result = getClass().getResource(fileName).getFile();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    return result;
+
+  }
+
+  @Override
+  public int getDefaultResponsePageSize()
+  {
+    if (dataColumns == null || dataColumns.isEmpty())
+    {
+      parseDataColumnsConfigFile();
+    }
+    return defaultResponsePageSize;
+  }
+
+}
similarity index 79%
rename from src/jalview/ws/uimodel/PDBRestRequest.java
rename to src/jalview/fts/core/FTSRestRequest.java
index 7bfc226..164b102 100644 (file)
@@ -1,6 +1,6 @@
 /*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 The Jalview Authors
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
  * 
  * This file is part of Jalview.
  * 
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
 
-package jalview.ws.uimodel;
+package jalview.fts.core;
 
 import jalview.bin.Cache;
 import jalview.datamodel.SequenceI;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+import jalview.fts.api.FTSDataColumnI;
 
 import java.util.Collection;
 
 /**
- * Represents the PDB request to be consumed by the PDBRestClient
+ * Represents the FTS request to be consumed by the FTSRestClient
  * 
  * @author tcnofoegbu
  *
  */
-public class PDBRestRequest
+public class FTSRestRequest
 {
   private String fieldToSearchBy;
 
@@ -56,9 +56,11 @@ public class PDBRestRequest
 
   private int responseSize;
 
+  private int offSet;
+
   private boolean isSortAscending;
 
-  private Collection<PDBDocField> wantedFields;
+  private Collection<FTSDataColumnI> wantedFields;
 
   public String getFieldToSearchBy()
   {
@@ -100,12 +102,13 @@ public class PDBRestRequest
     this.responseSize = responseSize;
   }
 
-  public Collection<PDBDocField> getWantedFields()
+  public Collection<FTSDataColumnI> getWantedFields()
   {
     return wantedFields;
   }
 
-  public void setWantedFields(Collection<PDBDocField> wantedFields)
+  public void setWantedFields(
+          Collection<FTSDataColumnI> wantedFields)
   {
     this.wantedFields = wantedFields;
   }
@@ -136,21 +139,6 @@ public class PDBRestRequest
     this.associatedSequence = associatedSequence;
   }
 
-  public String getQuery()
-  {
-    return fieldToSearchBy + searchTerm
-            + (isAllowEmptySeq() ? "" : " AND molecule_sequence:['' TO *]")
-            + (isAllowUnpublishedEntries() ? "" : " AND status:REL");
-  }
-
-  @Override
-  public String toString()
-  {
-    return "Query : " + getQuery() + " sort field: " + fieldToSortBy
-            + " isAsc: " + isAscending() + " Associated Seq : "
-            + associatedSequence;
-  }
-
   public boolean isAllowUnpublishedEntries()
   {
     return allowUnpublishedEntries;
@@ -190,4 +178,14 @@ public class PDBRestRequest
   {
     this.facetPivotMinCount = facetPivotMinCount;
   }
+
+  public int getOffSet()
+  {
+    return offSet;
+  }
+
+  public void setOffSet(int offSet)
+  {
+    this.offSet = offSet;
+  }
 }
diff --git a/src/jalview/fts/core/FTSRestResponse.java b/src/jalview/fts/core/FTSRestResponse.java
new file mode 100644 (file)
index 0000000..8078d43
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+package jalview.fts.core;
+
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+
+import java.util.Collection;
+
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableModel;
+
+/**
+ * Represents the response model generated by the FTSRestClient upon successful
+ * execution of a given FTS request
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class FTSRestResponse
+{
+  private int numberOfItemsFound;
+
+  private String responseTime;
+
+  private Collection<FTSData> searchSummary;
+
+  public int getNumberOfItemsFound()
+  {
+    return numberOfItemsFound;
+  }
+
+  public void setNumberOfItemsFound(int itemFound)
+  {
+    this.numberOfItemsFound = itemFound;
+  }
+
+  public String getResponseTime()
+  {
+    return responseTime;
+  }
+
+  public void setResponseTime(String responseTime)
+  {
+    this.responseTime = responseTime;
+  }
+
+  public Collection<FTSData> getSearchSummary()
+  {
+    return searchSummary;
+  }
+
+  public void setSearchSummary(Collection<FTSData> searchSummary)
+  {
+    this.searchSummary = searchSummary;
+  }
+
+  /**
+   * Convenience method to obtain a Table model for a given summary List based
+   * on the request parameters
+   * 
+   * @param request
+   *          the FTSRestRequest object which holds useful information for
+   *          creating a table model
+   * @param summariesList
+   *          the summary list which contains the data for populating the
+   *          table's rows
+   * @return the table model which was dynamically generated
+   */
+  public static DefaultTableModel getTableModel(FTSRestRequest request,
+          Collection<FTSData> summariesList)
+  {
+    final FTSDataColumnI[] cols = request.getWantedFields()
+            .toArray(new FTSDataColumnI[0]);
+    final int colOffset = request.getAssociatedSequence() == null ? 0 : 1;
+    DefaultTableModel tableModel = new DefaultTableModel()
+    {
+      @Override
+      public boolean isCellEditable(int row, int column)
+      {
+        return false;
+      }
+
+      @Override
+      public Class<?> getColumnClass(int columnIndex)
+      {
+        if (colOffset == 1 && columnIndex == 0)
+        {
+          return String.class;
+        }
+        return cols[columnIndex - colOffset].getDataType()
+                .getDataTypeClass();
+      }
+
+    };
+    if (request.getAssociatedSequence() != null)
+    {
+      tableModel.addColumn("Ref Sequence"); // Create sequence column header if
+      // exists in the request
+    }
+    for (FTSDataColumnI field : request
+            .getWantedFields())
+    {
+      tableModel.addColumn(field.getName()); // Create sequence column header if
+                                             // exists in the request
+    }
+
+    for (FTSData res : summariesList)
+    {
+      tableModel.addRow(res.getSummaryData()); // Populate table rows with
+                                               // summary list
+    }
+
+    return tableModel;
+  }
+
+  public static void configureTableColumn(JTable tbl_summary,
+          Collection<FTSDataColumnI> wantedFields)
+  {
+    for (FTSDataColumnI wantedField : wantedFields)
+    {
+      try
+      {
+        tbl_summary.getColumn(wantedField.getName()).setMinWidth(
+                wantedField.getMinWidth());
+        tbl_summary.getColumn(wantedField.getName()).setMaxWidth(
+                wantedField.getMaxWidth());
+        tbl_summary.getColumn(wantedField.getName()).setPreferredWidth(
+                wantedField.getPreferredWidth());
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
+      if (wantedField.getDataType().getDataTypeClass() == Double.class)
+      {
+        DecimalFormatTableCellRenderer dfr = new DecimalFormatTableCellRenderer(
+                wantedField.getDataType().isFormtted(),
+                wantedField.getDataType().getSignificantFigures());
+        tbl_summary.getColumn(wantedField.getName()).setCellRenderer(dfr);
+      }
+      else if (wantedField.getDataType().getDataTypeClass() == Integer.class)
+      {
+        DecimalFormatTableCellRenderer dfr = new DecimalFormatTableCellRenderer(
+                wantedField.getDataType().isFormtted(),
+                wantedField.getDataType().getSignificantFigures());
+        tbl_summary.getColumn(wantedField.getName()).setCellRenderer(dfr);
+      }
+    }
+  }
+
+
+}
diff --git a/src/jalview/fts/core/GFTSPanel.java b/src/jalview/fts/core/GFTSPanel.java
new file mode 100644 (file)
index 0000000..4dc61f0
--- /dev/null
@@ -0,0 +1,944 @@
+/*
+ * 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.
+ */
+
+package jalview.fts.core;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.GFTSPanelI;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.gui.Desktop;
+import jalview.gui.IProgressIndicator;
+import jalview.gui.JvSwingUtils;
+import jalview.gui.SequenceFetcher;
+import jalview.util.MessageManager;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.Timer;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableColumn;
+
+/**
+ * This class provides the swing GUI layout for FTS Panel and implements most of
+ * the contracts defined in GFSPanelI
+ * 
+ * @author tcnofoegbu
+ *
+ */
+
+@SuppressWarnings("serial")
+public abstract class GFTSPanel extends JPanel implements GFTSPanelI
+{
+  protected JInternalFrame mainFrame = new JInternalFrame(
+          getFTSFrameTitle());
+
+  protected IProgressIndicator progressIdicator;
+
+  protected JComboBox<FTSDataColumnI> cmb_searchTarget = new JComboBox<FTSDataColumnI>();
+
+  protected JButton btn_ok = new JButton();
+
+  protected JButton btn_back = new JButton();
+
+  protected JButton btn_cancel = new JButton();
+
+  protected JTextField txt_search = new JTextField(30);
+
+  protected SequenceFetcher seqFetcher;
+
+  protected Collection<FTSDataColumnI> wantedFields;
+
+  private String lastSearchTerm = "";
+
+  protected JButton btn_next_page = new JButton();
+
+  protected JButton btn_prev_page = new JButton();
+
+  protected StringBuilder errorWarning = new StringBuilder();
+
+  protected ImageIcon warningImage = new ImageIcon(getClass().getResource(
+          "/images/warning.gif"));
+
+  protected ImageIcon loadingImage = new ImageIcon(getClass().getResource(
+          "/images/loading.gif"));
+
+  protected ImageIcon balnkPlaceholderImage = new ImageIcon(getClass()
+          .getResource("/images/blank_16x16_placeholder.png"));
+
+  protected JLabel lbl_warning = new JLabel(warningImage);
+
+  protected JLabel lbl_loading = new JLabel(loadingImage);
+
+  protected JLabel lbl_blank = new JLabel(balnkPlaceholderImage);
+
+  private JTabbedPane tabbedPane = new JTabbedPane();
+
+  private JPanel pnl_actions = new JPanel();
+
+  private JPanel pnl_results = new JPanel(new CardLayout());
+
+  private JPanel pnl_inputs = new JPanel();
+
+  private BorderLayout mainLayout = new BorderLayout();
+
+  protected Object[] previousWantedFields;
+
+  protected int resultSetCount;
+
+  protected int totalResultSetCount;
+
+  protected int offSet;
+
+  protected int pageLimit;
+
+  protected HashSet<String> paginatorCart = new HashSet<String>();
+
+  protected static final DecimalFormat totalNumberformatter = new DecimalFormat(
+          "###,###");
+
+  private JTable tbl_summary = new JTable()
+  {
+    private boolean inLayout;
+
+    @Override
+    public boolean getScrollableTracksViewportWidth()
+    {
+      return hasExcessWidth();
+
+    }
+
+    @Override
+    public void doLayout()
+    {
+      if (hasExcessWidth())
+      {
+        autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
+      }
+      inLayout = true;
+      super.doLayout();
+      inLayout = false;
+      autoResizeMode = AUTO_RESIZE_OFF;
+    }
+
+    protected boolean hasExcessWidth()
+    {
+      return getPreferredSize().width < getParent().getWidth();
+    }
+
+    @Override
+    public void columnMarginChanged(ChangeEvent e)
+    {
+      if (isEditing())
+      {
+        removeEditor();
+      }
+      TableColumn resizingColumn = getTableHeader().getResizingColumn();
+      // Need to do this here, before the parent's
+      // layout manager calls getPreferredSize().
+      if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
+              && !inLayout)
+      {
+        resizingColumn.setPreferredWidth(resizingColumn.getWidth());
+      }
+      resizeAndRepaint();
+    }
+
+    @Override
+    public String getToolTipText(MouseEvent evt)
+    {
+      String toolTipText = null;
+      java.awt.Point pnt = evt.getPoint();
+      int rowIndex = rowAtPoint(pnt);
+      int colIndex = columnAtPoint(pnt);
+
+      try
+      {
+        if (getValueAt(rowIndex, colIndex) == null)
+        {
+          return null;
+        }
+        toolTipText = getValueAt(rowIndex, colIndex).toString();
+
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
+      toolTipText = (toolTipText == null ? null
+              : (toolTipText.length() > 500 ? JvSwingUtils.wrapTooltip(
+                      true, toolTipText.subSequence(0, 500) + "...")
+                      : JvSwingUtils.wrapTooltip(true, toolTipText)));
+
+      return toolTipText;
+    }
+  };
+  protected JScrollPane scrl_searchResult = new JScrollPane(tbl_summary);
+
+  public GFTSPanel()
+  {
+    try
+    {
+      jbInit();
+      mainFrame.invalidate();
+      mainFrame.pack();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Initializes the GUI default properties
+   * 
+   * @throws Exception
+   */
+  private void jbInit() throws Exception
+  {
+    lbl_warning.setVisible(false);
+    lbl_warning.setFont(new java.awt.Font("Verdana", 0, 12));
+    lbl_loading.setVisible(false);
+    lbl_loading.setFont(new java.awt.Font("Verdana", 0, 12));
+    lbl_blank.setVisible(true);
+    lbl_blank.setFont(new java.awt.Font("Verdana", 0, 12));
+
+    tbl_summary.setAutoCreateRowSorter(true);
+    tbl_summary.getTableHeader().setReorderingAllowed(false);
+    tbl_summary.addMouseListener(new MouseAdapter()
+    {
+      @Override
+      public void mouseClicked(MouseEvent e)
+      {
+        validateSelection();
+      }
+
+      @Override
+      public void mouseReleased(MouseEvent e)
+      {
+        validateSelection();
+      }
+    });
+    tbl_summary.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        validateSelection();
+        switch (evt.getKeyCode())
+        {
+        case KeyEvent.VK_ESCAPE: // escape key
+          btn_back_ActionPerformed();
+          break;
+        case KeyEvent.VK_ENTER: // enter key
+          if (btn_ok.isEnabled())
+          {
+            okAction();
+          }
+          evt.consume();
+          break;
+        case KeyEvent.VK_TAB: // tab key
+          if (evt.isShiftDown())
+          {
+            tabbedPane.requestFocus();
+          }
+          else
+          {
+            btn_back.requestFocus();
+          }
+          evt.consume();
+          break;
+        default:
+          return;
+        }
+      }
+    });
+
+    btn_back.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_back.setText(MessageManager.getString("action.back"));
+    btn_back.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_back_ActionPerformed();
+      }
+    });
+    btn_back.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          btn_back_ActionPerformed();
+        }
+      }
+    });
+
+    btn_ok.setEnabled(false);
+    btn_ok.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_ok.setText(MessageManager.getString("action.ok"));
+    btn_ok.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        okAction();
+      }
+    });
+    btn_ok.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          okAction();
+        }
+      }
+    });
+    btn_next_page.setEnabled(false);
+    btn_next_page.setToolTipText(MessageManager
+            .getString("label.next_page_tooltop"));
+    btn_next_page.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_next_page.setText(MessageManager.getString("action.next_page"));
+    btn_next_page.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        nextPageAction();
+      }
+    });
+    btn_next_page.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          nextPageAction();
+        }
+      }
+    });
+
+    btn_prev_page.setEnabled(false);
+    btn_prev_page.setToolTipText(MessageManager
+            .getString("label.prev_page_tooltop"));
+    btn_prev_page.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_prev_page.setText(MessageManager.getString("action.prev_page"));
+    btn_prev_page.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        prevPageAction();
+      }
+    });
+    btn_prev_page.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          prevPageAction();
+        }
+      }
+    });
+
+    if (isPaginationEnabled())
+    {
+      btn_prev_page.setVisible(true);
+      btn_next_page.setVisible(true);
+    }
+    else
+    {
+      btn_prev_page.setVisible(false);
+      btn_next_page.setVisible(false);
+    }
+
+    btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
+    btn_cancel.setText(MessageManager.getString("action.cancel"));
+    btn_cancel.addActionListener(new java.awt.event.ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        btn_cancel_ActionPerformed();
+      }
+    });
+    btn_cancel.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          btn_cancel_ActionPerformed();
+        }
+      }
+    });
+    scrl_searchResult.setPreferredSize(new Dimension(800, 400));
+
+    cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12));
+    cmb_searchTarget.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        String tooltipText;
+        if ("all".equalsIgnoreCase(getCmbSearchTarget().getSelectedItem()
+                .toString()))
+        {
+          tooltipText = MessageManager.getString("label.search_all");
+        }
+        else if ("pdb id".equalsIgnoreCase(getCmbSearchTarget()
+                .getSelectedItem().toString()))
+        {
+          tooltipText = MessageManager
+                  .getString("label.separate_multiple_accession_ids");
+        }
+        else
+        {
+          tooltipText = MessageManager.formatMessage(
+                  "label.separate_multiple_query_values",
+                  new Object[] { getCmbSearchTarget().getSelectedItem()
+                          .toString() });
+        }
+        txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
+                tooltipText));
+        searchAction(true);
+      }
+    });
+
+    populateCmbSearchTargetOptions();
+
+    txt_search.setFont(new java.awt.Font("Verdana", 0, 12));
+
+    txt_search.addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent e)
+      {
+        if (e.getKeyCode() == KeyEvent.VK_ENTER)
+        {
+          if (txt_search.getText() == null
+                  || txt_search.getText().isEmpty())
+          {
+            return;
+          }
+          String primaryKeyName = getFTSRestClient().getPrimaryKeyColumn()
+                  .getName();
+          if (primaryKeyName.equalsIgnoreCase(getCmbSearchTarget()
+                  .getSelectedItem().toString()))
+          {
+            transferToSequenceFetcher(txt_search.getText());
+          }
+        }
+      }
+    });
+
+    final DeferredTextInputListener listener = new DeferredTextInputListener(
+            1500,
+            new ActionListener()
+            {
+              @Override
+              public void actionPerformed(ActionEvent e)
+              {
+                if (!getTypedText().equalsIgnoreCase(lastSearchTerm))
+                {
+                  searchAction(true);
+                  paginatorCart.clear();
+                  lastSearchTerm = getTypedText();
+                }
+              }
+            }, false);
+    txt_search.getDocument().addDocumentListener(listener);
+    txt_search.addFocusListener(new FocusListener()
+    {
+      @Override
+      public void focusGained(FocusEvent e)
+      {
+        listener.start();
+      }
+
+      @Override
+      public void focusLost(FocusEvent e)
+      {
+//        listener.stop();
+      }
+    });
+
+    final String searchTabTitle = MessageManager
+            .getString("label.search_result");
+    final String configureCols = MessageManager
+            .getString("label.configure_displayed_columns");
+    ChangeListener changeListener = new ChangeListener()
+    {
+      @Override
+      public void stateChanged(ChangeEvent changeEvent)
+      {
+        JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
+                .getSource();
+        int index = sourceTabbedPane.getSelectedIndex();
+
+        btn_back.setVisible(true);
+        btn_cancel.setVisible(true);
+        btn_ok.setVisible(true);
+        if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
+        {
+          btn_back.setVisible(false);
+          btn_cancel.setVisible(false);
+          btn_ok.setVisible(false);
+          btn_back.setEnabled(false);
+          btn_cancel.setEnabled(false);
+          btn_ok.setEnabled(false);
+          btn_next_page.setEnabled(false);
+          btn_prev_page.setEnabled(false);
+          txt_search.setEnabled(false);
+          cmb_searchTarget.setEnabled(false);
+          previousWantedFields = getFTSRestClient()
+                  .getAllDefaulDisplayedDataColumns()
+                  .toArray(new Object[0]);
+        }
+        if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle))
+        {
+          btn_back.setEnabled(true);
+          btn_cancel.setEnabled(true);
+          refreshPaginatorState();
+          txt_search.setEnabled(true);
+          cmb_searchTarget.setEnabled(true);
+          if (wantedFieldsUpdated())
+          {
+            searchAction(true);
+            paginatorCart.clear();
+          }
+          else
+          {
+            validateSelection();
+          }
+        }
+      }
+    };
+    tabbedPane.addChangeListener(changeListener);
+    tabbedPane.setPreferredSize(new Dimension(800, 400));
+    tabbedPane.add(searchTabTitle, scrl_searchResult);
+    tabbedPane.add(configureCols, new FTSDataColumnPreferences(
+            PreferenceSource.SEARCH_SUMMARY, getFTSRestClient()));
+
+    pnl_actions.add(btn_back);
+    pnl_actions.add(btn_ok);
+    pnl_actions.add(btn_cancel);
+
+    pnl_results.add(tabbedPane);
+    pnl_inputs.add(cmb_searchTarget);
+    pnl_inputs.add(txt_search);
+    pnl_inputs.add(lbl_loading);
+    pnl_inputs.add(lbl_warning);
+    pnl_inputs.add(lbl_blank);
+    pnl_inputs.add(btn_prev_page);
+    pnl_inputs.add(btn_next_page);
+
+    this.setLayout(mainLayout);
+    this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
+    this.add(pnl_results, java.awt.BorderLayout.CENTER);
+    this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
+    mainFrame.setVisible(true);
+    mainFrame.setContentPane(this);
+    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+    Desktop.addInternalFrame(mainFrame, getFTSFrameTitle(), 900, 500);
+  }
+
+  public class DeferredTextInputListener implements DocumentListener
+  {
+    private final Timer swingTimer;
+
+    public DeferredTextInputListener(int timeOut, ActionListener listener,
+            boolean repeats)
+    {
+      swingTimer = new Timer(timeOut, listener);
+      swingTimer.setRepeats(repeats);
+    }
+
+    public void start()
+    {
+      swingTimer.start();
+    }
+
+    public void stop()
+    {
+      swingTimer.stop();
+    }
+
+    @Override
+    public void insertUpdate(DocumentEvent e)
+    {
+      swingTimer.restart();
+    }
+
+    @Override
+    public void removeUpdate(DocumentEvent e)
+    {
+      swingTimer.restart();
+    }
+
+    @Override
+    public void changedUpdate(DocumentEvent e)
+    {
+      swingTimer.restart();
+    }
+
+  }
+
+  public boolean wantedFieldsUpdated()
+  {
+    if (previousWantedFields == null)
+    {
+      return true;
+    }
+
+    return Arrays.equals(getFTSRestClient()
+            .getAllDefaulDisplayedDataColumns()
+            .toArray(new Object[0]), previousWantedFields) ? false
+            : true;
+
+  }
+
+  public void validateSelection()
+  {
+    if (tbl_summary.getSelectedRows().length > 0
+            || !paginatorCart.isEmpty())
+    {
+      btn_ok.setEnabled(true);
+    }
+    else
+    {
+      btn_ok.setEnabled(false);
+    }
+  }
+
+  public JComboBox<FTSDataColumnI> getCmbSearchTarget()
+  {
+    return cmb_searchTarget;
+  }
+
+  public JTextField getTxtSearch()
+  {
+    return txt_search;
+  }
+
+  public JInternalFrame getMainFrame()
+  {
+    return mainFrame;
+  }
+
+  protected void delayAndEnableActionButtons()
+  {
+    new Thread()
+    {
+      @Override
+      public void run()
+      {
+        try
+        {
+          Thread.sleep(1500);
+        } catch (InterruptedException e)
+        {
+          e.printStackTrace();
+        }
+        btn_ok.setEnabled(true);
+        btn_back.setEnabled(true);
+        btn_cancel.setEnabled(true);
+      }
+    }.start();
+  }
+
+  protected void checkForErrors()
+  {
+    lbl_warning.setVisible(false);
+    lbl_blank.setVisible(true);
+    if (errorWarning.length() > 0)
+    {
+      lbl_loading.setVisible(false);
+      lbl_blank.setVisible(false);
+      lbl_warning.setToolTipText(JvSwingUtils.wrapTooltip(true,
+              errorWarning.toString()));
+      lbl_warning.setVisible(true);
+    }
+  }
+
+  protected void btn_back_ActionPerformed()
+  {
+    mainFrame.dispose();
+    new SequenceFetcher(progressIdicator);
+  }
+
+  protected void disableActionButtons()
+  {
+    btn_ok.setEnabled(false);
+    btn_back.setEnabled(false);
+    btn_cancel.setEnabled(false);
+  }
+
+  protected void btn_cancel_ActionPerformed()
+  {
+    mainFrame.dispose();
+  }
+
+  /**
+   * Populates search target combo-box options
+   */
+  public void populateCmbSearchTargetOptions()
+  {
+    List<FTSDataColumnI> searchableTargets = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      Collection<FTSDataColumnI> foundFTSTargets = getFTSRestClient()
+              .getSearchableDataColumns();
+      searchableTargets.addAll(foundFTSTargets);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+
+    Collections.sort(searchableTargets, new Comparator<FTSDataColumnI>()
+    {
+      @Override
+      public int compare(FTSDataColumnI o1, FTSDataColumnI o2)
+      {
+        return o1.getName().compareTo(o2.getName());
+      }
+    });
+
+    for (FTSDataColumnI searchTarget : searchableTargets)
+    {
+      cmb_searchTarget.addItem(searchTarget);
+    }
+  }
+
+
+  public void transferToSequenceFetcher(String ids)
+  {
+    // mainFrame.dispose();
+    seqFetcher.getTextArea().setText(ids);
+    Thread worker = new Thread(seqFetcher);
+    worker.start();
+  }
+
+  @Override
+  public String getTypedText()
+  {
+    return txt_search.getText().trim();
+  }
+
+  @Override
+  public JTable getResultTable()
+  {
+    return tbl_summary;
+  }
+
+  public void reset()
+  {
+    lbl_loading.setVisible(false);
+    errorWarning.setLength(0);
+    lbl_warning.setVisible(false);
+    lbl_blank.setVisible(true);
+    btn_ok.setEnabled(false);
+    mainFrame.setTitle(getFTSFrameTitle());
+    referesh();
+    tbl_summary.setModel(new DefaultTableModel());
+    tbl_summary.setVisible(false);
+  }
+
+  @Override
+  public void setPrevPageButtonEnabled(boolean isEnabled)
+  {
+    btn_prev_page.setEnabled(isEnabled);
+  }
+
+  @Override
+  public void setNextPageButtonEnabled(boolean isEnabled)
+  {
+    btn_next_page.setEnabled(isEnabled);
+  }
+
+  @Override
+  public void setErrorMessage(String message)
+  {
+    errorWarning.append(message);
+  }
+
+  @Override
+  public void updateSearchFrameTitle(String title)
+  {
+    mainFrame.setTitle(title);
+  }
+
+  @Override
+  public void setSearchInProgress(Boolean isSearchInProgress)
+  {
+    lbl_blank.setVisible(!isSearchInProgress);
+    lbl_loading.setVisible(isSearchInProgress);
+  }
+
+  @Override
+  public void prevPageAction()
+  {
+    updatePaginatorCart();
+    if (offSet >= pageLimit)
+    {
+      offSet = offSet - pageLimit;
+      searchAction(false);
+    }
+    else
+    {
+      refreshPaginatorState();
+    }
+  }
+
+  @Override
+  public void nextPageAction()
+  {
+    updatePaginatorCart();
+    offSet = offSet + pageLimit;
+    searchAction(false);
+  }
+
+  public void updatePaginatorCart()
+  {
+    int primaryKeyColIndex = 0;
+    JTable resultTable = getResultTable();
+    int totalRows = resultTable.getRowCount();
+    try
+    {
+      primaryKeyColIndex = getFTSRestClient().getPrimaryKeyColumIndex(
+              wantedFields, false);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+
+    for (int row = 0; row < totalRows; row++)
+    {
+      String id = (String) resultTable.getValueAt(row, primaryKeyColIndex);
+      if (paginatorCart.contains(id))
+      {
+        paginatorCart.remove(id);
+      }
+    }
+    int[] selectedRows = resultTable.getSelectedRows();
+    for (int summaryRow : selectedRows)
+    {
+      String idStr = resultTable.getValueAt(summaryRow,
+              primaryKeyColIndex).toString();
+      paginatorCart.add(idStr);
+    }
+    // System.out.println("Paginator shopping cart size : "
+    // + paginatorCart.size());
+  }
+
+  public void updateSummaryTableSelections()
+  {
+    JTable resultTable = getResultTable();
+    if (paginatorCart.isEmpty())
+    {
+      return;
+    }
+    int primaryKeyColIndex = 0;
+    try
+    {
+      primaryKeyColIndex = getFTSRestClient().getPrimaryKeyColumIndex(
+              wantedFields, false);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    // System.out.println(">>>>>> got here : 1");
+    int totalRows = resultTable.getRowCount();
+    // resultTable.clearSelection();
+    for (int row = 0; row < totalRows; row++)
+    {
+      String id = (String) resultTable.getValueAt(row, primaryKeyColIndex);
+      if (paginatorCart.contains(id))
+      {
+        resultTable.addRowSelectionInterval(row, row);
+      }
+    }
+    validateSelection();
+  }
+  public void refreshPaginatorState()
+  {
+    // System.out.println("resultSet count : " + resultSetCount);
+    // System.out.println("offSet : " + offSet);
+    // System.out.println("page limit : " + pageLimit);
+    setPrevPageButtonEnabled(false);
+    setNextPageButtonEnabled(false);
+    if (resultSetCount == 0 && pageLimit == 0)
+    {
+      return;
+    }
+    if (resultSetCount >= pageLimit)
+    {
+      setNextPageButtonEnabled(true);
+    }
+    if (offSet >= pageLimit)
+    {
+      setPrevPageButtonEnabled(true);
+    }
+  }
+  public void referesh()
+  {
+    mainFrame.setTitle(getFTSFrameTitle());
+  }
+
+}
diff --git a/src/jalview/fts/service/pdb/PDBFTSPanel.java b/src/jalview/fts/service/pdb/PDBFTSPanel.java
new file mode 100644 (file)
index 0000000..0dd1369
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+package jalview.fts.service.pdb;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.core.GFTSPanel;
+import jalview.gui.SequenceFetcher;
+import jalview.util.MessageManager;
+
+import java.util.HashSet;
+
+@SuppressWarnings("serial")
+public class PDBFTSPanel extends GFTSPanel
+{
+  private static String defaultFTSFrameTitle = MessageManager
+          .getString("label.pdb_sequence_fetcher");
+
+  private String ftsFrameTitle = defaultFTSFrameTitle;
+
+  public PDBFTSPanel(SequenceFetcher seqFetcher)
+  {
+    pageLimit = PDBFTSRestClient.getInstance().getDefaultResponsePageSize();
+    this.seqFetcher = seqFetcher;
+    this.progressIdicator = (seqFetcher == null) ? null : seqFetcher
+            .getProgressIndicator();
+  }
+
+
+  @Override
+  public void searchAction(boolean isFreshSearch)
+  {
+    if (isFreshSearch)
+    {
+      offSet = 0;
+    }
+    new Thread()
+    {
+      @Override
+      public void run()
+      {
+        ftsFrameTitle = defaultFTSFrameTitle;
+        reset();
+        boolean allowEmptySequence = false;
+        if (getTypedText().length() > 0)
+        {
+          setSearchInProgress(true);
+          long startTime = System.currentTimeMillis();
+
+          String searchTarget = ((FTSDataColumnI) cmb_searchTarget
+                  .getSelectedItem()).getCode();
+          wantedFields = PDBFTSRestClient.getInstance()
+                  .getAllDefaulDisplayedDataColumns();
+          String searchTerm = decodeSearchTerm(txt_search.getText(),
+                  searchTarget);
+
+          FTSRestRequest request = new FTSRestRequest();
+          request.setAllowEmptySeq(allowEmptySequence);
+          request.setResponseSize(100);
+          request.setFieldToSearchBy("(" + searchTarget + ":");
+          request.setSearchTerm(searchTerm + ")");
+          request.setOffSet(offSet);
+          request.setWantedFields(wantedFields);
+          FTSRestClientI pdbRestCleint = PDBFTSRestClient.getInstance();
+          FTSRestResponse resultList;
+          try
+          {
+            resultList = pdbRestCleint.executeRequest(request);
+          } catch (Exception e)
+          {
+            setErrorMessage(e.getMessage());
+            checkForErrors();
+            return;
+          }
+
+          if (resultList.getSearchSummary() != null
+                  && resultList.getSearchSummary().size() > 0)
+          {
+            getResultTable().setModel(
+                    FTSRestResponse.getTableModel(request,
+                    resultList.getSearchSummary()));
+            FTSRestResponse.configureTableColumn(getResultTable(),
+                    wantedFields);
+            getResultTable().setVisible(true);
+          }
+
+          long endTime = System.currentTimeMillis();
+          totalResultSetCount = resultList.getNumberOfItemsFound();
+          resultSetCount = resultList.getSearchSummary() == null ? 0
+                  : resultList.getSearchSummary().size();
+          String result = (resultSetCount > 0) ? MessageManager
+                  .getString("label.results") : MessageManager
+                  .getString("label.result");
+         
+          if (isPaginationEnabled() && resultSetCount > 0)
+          {
+            updateSearchFrameTitle(defaultFTSFrameTitle + " - " + result
+                    + " "
+                    + totalNumberformatter.format((Number) (offSet + 1))
+                    + " to "
+                    + totalNumberformatter
+                            .format((Number) (offSet + resultSetCount))
+                    + " of "
+                    + totalNumberformatter
+                            .format((Number) totalResultSetCount)
+                    + " " + " (" + (endTime - startTime) + " milli secs)");
+          }
+          else
+          {
+            updateSearchFrameTitle(defaultFTSFrameTitle + " - "
+                    + resultSetCount + " " + result + " ("
+                    + (endTime - startTime) + " milli secs)");
+          }
+          
+          setSearchInProgress(false);
+          refreshPaginatorState();
+          updateSummaryTableSelections();
+        }
+      }
+    }.start();
+  }
+
+  public static String decodeSearchTerm(String enteredText,
+          String targetField)
+  {
+    String foundSearchTerms = enteredText;
+    StringBuilder foundSearchTermsBuilder = new StringBuilder();
+    if (enteredText.contains(";"))
+    {
+      String[] searchTerms = enteredText.split(";");
+      for (String searchTerm : searchTerms)
+      {
+        if (searchTerm.contains(":"))
+        {
+          foundSearchTermsBuilder.append(targetField).append(":")
+                  .append(searchTerm.split(":")[0]).append(" OR ");
+        }
+        else
+        {
+          foundSearchTermsBuilder.append(targetField).append(":")
+                  .append(searchTerm).append(" OR ");
+        }
+      }
+      int endIndex = foundSearchTermsBuilder.lastIndexOf(" OR ");
+      foundSearchTerms = foundSearchTermsBuilder.toString();
+      if (foundSearchTerms.contains(" OR "))
+      {
+        foundSearchTerms = foundSearchTerms.substring(
+                targetField.length() + 1, endIndex);
+      }
+    }
+    else if (enteredText.contains(":"))
+    {
+      foundSearchTerms = foundSearchTerms.split(":")[0];
+    }
+    return foundSearchTerms;
+  }
+
+  @Override
+  public void okAction()
+  {
+    // mainFrame.dispose();
+    disableActionButtons();
+    StringBuilder selectedIds = new StringBuilder();
+    HashSet<String> selectedIdsSet = new HashSet<String>();
+    int primaryKeyColIndex = 0;
+    try
+    {
+      primaryKeyColIndex = getFTSRestClient().getPrimaryKeyColumIndex(
+              wantedFields,
+              false);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    int[] selectedRows = getResultTable().getSelectedRows();
+    String searchTerm = txt_search.getText();
+    for (int summaryRow : selectedRows)
+    {
+      String idStr = getResultTable().getValueAt(summaryRow,
+              primaryKeyColIndex)
+              .toString();
+      selectedIdsSet.add(getPDBIdwithSpecifiedChain(idStr, searchTerm));
+    }
+
+    for (String idStr : paginatorCart)
+    {
+      selectedIdsSet.add(getPDBIdwithSpecifiedChain(idStr, searchTerm));
+    }
+
+    for (String selectedId : selectedIdsSet)
+    {
+      selectedIds.append(selectedId).append(";");
+    }
+
+    String ids = selectedIds.toString();
+    // System.out.println(">>>>>>>>>>>>>>>> selected Ids: " + ids);
+    seqFetcher.getTextArea().setText(ids);
+    Thread worker = new Thread(seqFetcher);
+    worker.start();
+    delayAndEnableActionButtons();
+  }
+
+
+  public static String getPDBIdwithSpecifiedChain(String pdbId,
+          String searchTerm)
+  {
+    String pdbIdWithChainCode = "";
+    if (searchTerm.contains(";"))
+    {
+      String[] foundTerms = searchTerm.split(";");
+      for (String foundTerm : foundTerms)
+      {
+        if (foundTerm.contains(pdbId))
+        {
+          pdbIdWithChainCode = foundTerm;
+        }
+      }
+    }
+    else if (searchTerm.contains(pdbId))
+    {
+      pdbIdWithChainCode = searchTerm;
+    }
+    else
+    {
+      pdbIdWithChainCode = pdbId;
+    }
+    return pdbIdWithChainCode;
+  }
+
+  @Override
+  public FTSRestClientI getFTSRestClient()
+  {
+    return PDBFTSRestClient.getInstance();
+  }
+
+  @Override
+  public String getFTSFrameTitle()
+  {
+    return ftsFrameTitle;
+  }
+
+  @Override
+  public boolean isPaginationEnabled()
+  {
+    return true;
+  }
+
+}
diff --git a/src/jalview/fts/service/pdb/PDBFTSRestClient.java b/src/jalview/fts/service/pdb/PDBFTSRestClient.java
new file mode 100644 (file)
index 0000000..93a7df3
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * 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.
+ */
+package jalview.fts.service.pdb;
+
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestClient;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.util.MessageManager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+import javax.ws.rs.core.MediaType;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+
+/**
+ * A rest client for querying the Search endpoint of the PDB API
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class PDBFTSRestClient extends FTSRestClient
+{
+
+  private static FTSRestClientI instance = null;
+
+  public static final String PDB_SEARCH_ENDPOINT = "http://www.ebi.ac.uk/pdbe/search/pdb/select?";
+
+  protected PDBFTSRestClient()
+  {
+  }
+
+  /**
+   * Takes a PDBRestRequest object and returns a response upon execution
+   * 
+   * @param pdbRestRequest
+   *          the PDBRestRequest instance to be processed
+   * @return the pdbResponse object for the given request
+   * @throws Exception
+   */
+  @Override
+  public FTSRestResponse executeRequest(FTSRestRequest pdbRestRequest)
+          throws Exception
+  {
+    try
+    {
+      ClientConfig clientConfig = new DefaultClientConfig();
+      Client client = Client.create(clientConfig);
+
+      String wantedFields = getDataColumnsFieldsAsCommaDelimitedString(pdbRestRequest
+              .getWantedFields());
+      int responseSize = (pdbRestRequest.getResponseSize() == 0) ? getDefaultResponsePageSize()
+              : pdbRestRequest.getResponseSize();
+      int offSet = pdbRestRequest.getOffSet();
+      String sortParam = null;
+      if (pdbRestRequest.getFieldToSortBy() == null
+              || pdbRestRequest.getFieldToSortBy().trim().isEmpty())
+      {
+        sortParam = "";
+      }
+      else
+      {
+        if (pdbRestRequest.getFieldToSortBy()
+                .equalsIgnoreCase("Resolution"))
+        {
+          sortParam = pdbRestRequest.getFieldToSortBy()
+                  + (pdbRestRequest.isAscending() ? " asc" : " desc");
+        }
+        else
+        {
+          sortParam = pdbRestRequest.getFieldToSortBy()
+                  + (pdbRestRequest.isAscending() ? " desc" : " asc");
+        }
+      }
+
+      String facetPivot = (pdbRestRequest.getFacetPivot() == null || pdbRestRequest
+              .getFacetPivot().isEmpty()) ? "" : pdbRestRequest
+              .getFacetPivot();
+      String facetPivotMinCount = String.valueOf(pdbRestRequest
+              .getFacetPivotMinCount());
+
+      String query = pdbRestRequest.getFieldToSearchBy()
+              + pdbRestRequest.getSearchTerm()
+              + (pdbRestRequest.isAllowEmptySeq() ? ""
+                      : " AND molecule_sequence:['' TO *]")
+              + (pdbRestRequest.isAllowUnpublishedEntries() ? ""
+                      : " AND status:REL");
+
+      // Build request parameters for the REST Request
+      WebResource webResource = null;
+      if (pdbRestRequest.isFacet())
+      {
+        webResource = client.resource(PDB_SEARCH_ENDPOINT)
+                .queryParam("wt", "json").queryParam("fl", wantedFields)
+                .queryParam("rows", String.valueOf(responseSize))
+                .queryParam("q", query)
+                .queryParam("start", String.valueOf(offSet))
+                .queryParam("sort", sortParam).queryParam("facet", "true")
+                .queryParam("facet.pivot", facetPivot)
+                .queryParam("facet.pivot.mincount", facetPivotMinCount);
+      }
+      else
+      {
+        webResource = client.resource(PDB_SEARCH_ENDPOINT)
+                .queryParam("wt", "json").queryParam("fl", wantedFields)
+                .queryParam("rows", String.valueOf(responseSize))
+                .queryParam("start", String.valueOf(offSet))
+                .queryParam("q", query)
+                .queryParam("sort", sortParam);
+      }
+      // Execute the REST request
+      ClientResponse clientResponse = webResource.accept(
+              MediaType.APPLICATION_JSON).get(ClientResponse.class);
+
+      // Get the JSON string from the response object
+      String responseString = clientResponse.getEntity(String.class);
+      // System.out.println("query >>>>>>> " + pdbRestRequest.toString());
+
+      // Check the response status and report exception if one occurs
+      if (clientResponse.getStatus() != 200)
+      {
+        String errorMessage = "";
+        if (clientResponse.getStatus() == 400)
+        {
+          errorMessage = parseJsonExceptionString(responseString);
+          throw new Exception(errorMessage);
+        }
+        else
+        {
+          errorMessage = getMessageByHTTPStatusCode(clientResponse
+.getStatus(), "PDB");
+          throw new Exception(errorMessage);
+        }
+      }
+
+      // Make redundant objects eligible for garbage collection to conserve
+      // memory
+      clientResponse = null;
+      client = null;
+
+      // Process the response and return the result to the caller.
+      return parsePDBJsonResponse(responseString, pdbRestRequest);
+    } catch (Exception e)
+    {
+      String exceptionMsg = e.getMessage();
+      if (exceptionMsg.contains("SocketException"))
+      {
+        // No internet connection
+        throw new Exception(
+                MessageManager
+                        .getString("exception.unable_to_detect_internet_connection"));
+      }
+      else if (exceptionMsg.contains("UnknownHostException"))
+      {
+        // The server 'www.ebi.ac.uk' is unreachable
+        throw new Exception(MessageManager.formatMessage(
+                "exception.fts_server_unreachable", "PDB Solr"));
+      }
+      else
+      {
+        throw e;
+      }
+    }
+  }
+
+
+  /**
+   * Process error response from PDB server if/when one occurs.
+   * 
+   * @param jsonResponse
+   *          the JSON string containing error message from the server
+   * @return the processed error message from the JSON string
+   */
+  public static String parseJsonExceptionString(String jsonErrorResponse)
+  {
+    StringBuilder errorMessage = new StringBuilder(
+            "\n============= PDB Rest Client RunTime error =============\n");
+
+    try
+    {
+      JSONParser jsonParser = new JSONParser();
+      JSONObject jsonObj = (JSONObject) jsonParser.parse(jsonErrorResponse);
+      JSONObject errorResponse = (JSONObject) jsonObj.get("error");
+
+      JSONObject responseHeader = (JSONObject) jsonObj
+              .get("responseHeader");
+      JSONObject paramsObj = (JSONObject) responseHeader.get("params");
+      String status = responseHeader.get("status").toString();
+      String message = errorResponse.get("msg").toString();
+      String query = paramsObj.get("q").toString();
+      String fl = paramsObj.get("fl").toString();
+
+      errorMessage.append("Status: ").append(status).append("\n");
+      errorMessage.append("Message: ").append(message).append("\n");
+      errorMessage.append("query: ").append(query).append("\n");
+      errorMessage.append("fl: ").append(fl).append("\n");
+
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+    }
+    return errorMessage.toString();
+  }
+
+  /**
+   * Parses the JSON response string from PDB REST API. The response is dynamic
+   * hence, only fields specifically requested for in the 'wantedFields'
+   * parameter is fetched/processed
+   * 
+   * @param pdbJsonResponseString
+   *          the JSON string to be parsed
+   * @param pdbRestRequest
+   *          the request object which contains parameters used to process the
+   *          JSON string
+   * @return
+   */
+  @SuppressWarnings("unchecked")
+  public static FTSRestResponse parsePDBJsonResponse(
+          String pdbJsonResponseString, FTSRestRequest pdbRestRequest)
+  {
+    FTSRestResponse searchResult = new FTSRestResponse();
+    List<FTSData> result = null;
+    try
+    {
+      JSONParser jsonParser = new JSONParser();
+      JSONObject jsonObj = (JSONObject) jsonParser
+              .parse(pdbJsonResponseString);
+
+      JSONObject pdbResponse = (JSONObject) jsonObj.get("response");
+      String queryTime = ((JSONObject) jsonObj.get("responseHeader")).get(
+              "QTime").toString();
+      int numFound = Integer
+              .valueOf(pdbResponse.get("numFound").toString());
+      if (numFound > 0)
+      {
+        result = new ArrayList<FTSData>();
+        JSONArray docs = (JSONArray) pdbResponse.get("docs");
+        for (Iterator<JSONObject> docIter = docs.iterator(); docIter
+                .hasNext();)
+        {
+          JSONObject doc = docIter.next();
+          result.add(getFTSData(doc, pdbRestRequest));
+        }
+        searchResult.setNumberOfItemsFound(numFound);
+        searchResult.setResponseTime(queryTime);
+        searchResult.setSearchSummary(result);
+      }
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+    }
+    return searchResult;
+  }
+
+  public static FTSData getFTSData(JSONObject pdbJsonDoc,
+          FTSRestRequest request)
+  {
+
+    String primaryKey = null;
+
+    Object[] summaryRowData;
+
+    SequenceI associatedSequence;
+
+    Collection<FTSDataColumnI> diplayFields = request.getWantedFields();
+    SequenceI associatedSeq = request.getAssociatedSequence();
+    int colCounter = 0;
+    summaryRowData = new Object[(associatedSeq != null) ? diplayFields
+            .size() + 1 : diplayFields.size()];
+    if (associatedSeq != null)
+    {
+      associatedSequence = associatedSeq;
+      summaryRowData[0] = associatedSequence;
+      colCounter = 1;
+    }
+
+    for (FTSDataColumnI field : diplayFields)
+    {
+      String fieldData = (pdbJsonDoc.get(field.getCode()) == null) ? ""
+              : pdbJsonDoc.get(field.getCode()).toString();
+      if (field.isPrimaryKeyColumn())
+      {
+        primaryKey = fieldData;
+        summaryRowData[colCounter++] = primaryKey;
+      }
+      else if (fieldData == null || fieldData.isEmpty())
+      {
+        summaryRowData[colCounter++] = null;
+      }
+      else
+      {
+        try
+        {
+          summaryRowData[colCounter++] = (field.getDataType()
+                  .getDataTypeClass() == Integer.class) ? Integer
+                  .valueOf(fieldData)
+ : (field.getDataType()
+                  .getDataTypeClass() == Double.class) ? Double
+                          .valueOf(fieldData)
+ : sanitiseData(fieldData);
+        } catch (Exception e)
+        {
+          e.printStackTrace();
+            System.out.println("offending value:" + fieldData);
+        }
+      }
+    }
+
+    final String primaryKey1 = primaryKey;
+
+    final Object[] summaryRowData1 = summaryRowData;
+    return new FTSData()
+    {
+      @Override
+      public Object[] getSummaryData()
+      {
+        return summaryRowData1;
+      }
+
+      @Override
+      public Object getPrimaryKey()
+      {
+        return primaryKey1;
+      }
+
+      /**
+       * Returns a string representation of this object;
+       */
+      @Override
+      public String toString()
+      {
+        StringBuilder summaryFieldValues = new StringBuilder();
+        for (Object summaryField : summaryRowData1)
+        {
+          summaryFieldValues.append(
+                  summaryField == null ? " " : summaryField.toString())
+                  .append("\t");
+        }
+        return summaryFieldValues.toString();
+      }
+
+      /**
+       * Returns hash code value for this object
+       */
+      @Override
+      public int hashCode()
+      {
+        return Objects.hash(primaryKey1, this.toString());
+      }
+
+      @Override
+      public boolean equals(Object that)
+      {
+        return this.toString().equals(that.toString());
+      }
+    };
+  }
+
+  private static String sanitiseData(String data)
+  {
+    String cleanData = data.replaceAll("\\[\"", "").replaceAll("\\]\"", "")
+            .replaceAll("\\[", "").replaceAll("\\]", "")
+            .replaceAll("\",\"", ", ").replaceAll("\"", "");
+    return cleanData;
+  }
+
+  @Override
+  public String getColumnDataConfigFileName()
+  {
+    return "/fts/pdb_data_columns.txt";
+  }
+
+
+  public static FTSRestClientI getInstance()
+  {
+    if (instance == null)
+    {
+      instance = new PDBFTSRestClient();
+    }
+    return instance;
+  }
+}
diff --git a/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java b/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java
new file mode 100644 (file)
index 0000000..dfdf715
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * 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.
+ */
+
+package jalview.fts.service.uniprot;
+
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestClient;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.util.MessageManager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+import javax.ws.rs.core.MediaType;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+
+public class UniProtFTSRestClient extends FTSRestClient
+{
+  private static FTSRestClientI instance = null;
+
+  public static final String UNIPROT_SEARCH_ENDPOINT = "http://www.uniprot.org/uniprot/?";
+
+  @Override
+  public FTSRestResponse executeRequest(FTSRestRequest uniportRestRequest)
+          throws Exception
+  {
+    try
+    {
+      ClientConfig clientConfig = new DefaultClientConfig();
+      Client client = Client.create(clientConfig);
+
+      String wantedFields = getDataColumnsFieldsAsCommaDelimitedString(uniportRestRequest
+              .getWantedFields());
+      int responseSize = (uniportRestRequest.getResponseSize() == 0) ? getDefaultResponsePageSize()
+              : uniportRestRequest.getResponseSize();
+
+      int offSet = uniportRestRequest.getOffSet();
+      String query;
+      if (isAdvancedQuery(uniportRestRequest.getSearchTerm()))
+      {
+        query = uniportRestRequest.getSearchTerm();
+      }
+      else
+      {
+        query = uniportRestRequest.getFieldToSearchBy().equalsIgnoreCase(
+                "Search All") ? uniportRestRequest.getSearchTerm()
+                + " or mnemonic:" + uniportRestRequest.getSearchTerm()
+                : uniportRestRequest.getFieldToSearchBy() + ":"
+                        + uniportRestRequest.getSearchTerm();
+      }
+
+      WebResource webResource = null;
+      webResource = client.resource(UNIPROT_SEARCH_ENDPOINT)
+              .queryParam("format", "tab")
+              .queryParam("columns", wantedFields)
+              .queryParam("limit", String.valueOf(responseSize))
+              .queryParam("offset", String.valueOf(offSet))
+              .queryParam("sort", "score").queryParam("query", query);
+      // Execute the REST request
+      ClientResponse clientResponse = webResource.accept(
+              MediaType.TEXT_PLAIN).get(ClientResponse.class);
+      String uniProtTabDelimittedResponseString = clientResponse
+              .getEntity(String.class);
+      // Make redundant objects eligible for garbage collection to conserve
+      // memory
+      // System.out.println(">>>>> response : "
+      // + uniProtTabDelimittedResponseString);
+      if (clientResponse.getStatus() != 200)
+      {
+        String errorMessage = getMessageByHTTPStatusCode(
+                clientResponse.getStatus(), "Uniprot");
+        throw new Exception(errorMessage);
+
+      }
+      int xTotalResults = Integer.valueOf(clientResponse.getHeaders()
+              .get("X-Total-Results").get(0));
+      clientResponse = null;
+      client = null;
+      return parseUniprotResponse(uniProtTabDelimittedResponseString,
+              uniportRestRequest, xTotalResults);
+    } catch (Exception e)
+    {
+      String exceptionMsg = e.getMessage();
+      if (exceptionMsg.contains("SocketException"))
+      {
+        // No internet connection
+        throw new Exception(
+                MessageManager
+                        .getString("exception.unable_to_detect_internet_connection"));
+      }
+      else if (exceptionMsg.contains("UnknownHostException"))
+      {
+        // The server 'http://www.uniprot.org' is unreachable
+        throw new Exception(MessageManager.formatMessage(
+                "exception.fts_server_unreachable", "Uniprot"));
+      }
+      else
+      {
+        throw e;
+      }
+    }
+  }
+
+  public boolean isAdvancedQuery(String query)
+  {
+    if (query.contains(" AND ") || query.contains(" OR ")
+            || query.contains(" NOT ") || query.contains(" ! ")
+            || query.contains(" || ") || query.contains(" && ")
+            || query.contains(":") || query.contains("-"))
+    {
+      return true;
+    }
+    return false;
+  }
+
+  public FTSRestResponse parseUniprotResponse(
+          String uniProtTabDelimittedResponseString,
+          FTSRestRequest uniprotRestRequest, int xTotalResults)
+  {
+    FTSRestResponse searchResult = new FTSRestResponse();
+    List<FTSData> result = null;
+    if (uniProtTabDelimittedResponseString == null
+            || uniProtTabDelimittedResponseString.trim().isEmpty())
+    {
+      searchResult.setNumberOfItemsFound(0);
+      return searchResult;
+    }
+    String[] foundDataRow = uniProtTabDelimittedResponseString.split("\n");
+    if (foundDataRow != null && foundDataRow.length > 0)
+    {
+      result = new ArrayList<FTSData>();
+      String titleRow = getDataColumnsFieldsAsTabDelimitedString(uniprotRestRequest
+              .getWantedFields());
+      // System.out.println(">>>>Title row : " + titleRow);
+      for (String dataRow : foundDataRow)
+      {
+        if (dataRow.equalsIgnoreCase(titleRow))
+        {
+          // System.out.println(">>>>>>>>>> matched!!!");
+          continue;
+        }
+        // System.out.println(dataRow);
+        result.add(getFTSData(dataRow, uniprotRestRequest));
+      }
+      searchResult.setNumberOfItemsFound(xTotalResults);
+      searchResult.setSearchSummary(result);
+    }
+    return searchResult;
+  }
+
+  /**
+   * Takes a collection of FTSDataColumnI and converts its 'code' values into a
+   * tab delimited string.
+   * 
+   * @param dataColumnFields
+   *          the collection of FTSDataColumnI to process
+   * @return the generated comma delimited string from the supplied
+   *         FTSDataColumnI collection
+   */
+  private String getDataColumnsFieldsAsTabDelimitedString(
+          Collection<FTSDataColumnI> dataColumnFields)
+  {
+    String result = "";
+    if (dataColumnFields != null && !dataColumnFields.isEmpty())
+    {
+      StringBuilder returnedFields = new StringBuilder();
+      for (FTSDataColumnI field : dataColumnFields)
+      {
+        if (field.getName().equalsIgnoreCase("Uniprot Id"))
+        {
+          returnedFields.append("\t").append("Entry");
+        }
+        else
+        {
+          returnedFields.append("\t").append(field.getName());
+        }
+      }
+      returnedFields.deleteCharAt(0);
+      result = returnedFields.toString();
+    }
+    return result;
+  }
+
+  public static FTSData getFTSData(String tabDelimittedDataStr,
+          FTSRestRequest request)
+  {
+    String primaryKey = null;
+
+    Object[] summaryRowData;
+
+    Collection<FTSDataColumnI> diplayFields = request.getWantedFields();
+    int colCounter = 0;
+    summaryRowData = new Object[diplayFields.size()];
+    String[] columns = tabDelimittedDataStr.split("\t");
+    for (FTSDataColumnI field : diplayFields)
+    {
+      try
+      {
+        String fieldData = columns[colCounter];
+        if (field.isPrimaryKeyColumn())
+        {
+          primaryKey = fieldData;
+          summaryRowData[colCounter++] = primaryKey;
+        }
+        else if (fieldData == null || fieldData.isEmpty())
+        {
+          summaryRowData[colCounter++] = null;
+        }
+        else
+        {
+          try
+          {
+            summaryRowData[colCounter++] = (field.getDataType()
+                    .getDataTypeClass() == Integer.class) ? Integer
+                    .valueOf(fieldData)
+ : (field.getDataType()
+                    .getDataTypeClass() == Double.class) ? Double
+                            .valueOf(fieldData) : fieldData;
+          } catch (Exception e)
+          {
+            e.printStackTrace();
+              System.out.println("offending value:" + fieldData);
+          }
+        }
+      } catch (Exception e)
+      {
+        // e.printStackTrace();
+      }
+    }
+
+    final String primaryKey1 = primaryKey;
+
+    final Object[] summaryRowData1 = summaryRowData;
+    return new FTSData()
+    {
+      @Override
+      public Object[] getSummaryData()
+      {
+        return summaryRowData1;
+      }
+
+      @Override
+      public Object getPrimaryKey()
+      {
+        return primaryKey1;
+      }
+
+      /**
+       * Returns a string representation of this object;
+       */
+      @Override
+      public String toString()
+      {
+        StringBuilder summaryFieldValues = new StringBuilder();
+        for (Object summaryField : summaryRowData1)
+        {
+          summaryFieldValues.append(
+                  summaryField == null ? " " : summaryField.toString())
+                  .append("\t");
+        }
+        return summaryFieldValues.toString();
+      }
+
+      /**
+       * Returns hash code value for this object
+       */
+      @Override
+      public int hashCode()
+      {
+        return Objects.hash(primaryKey1, this.toString());
+      }
+
+      @Override
+      public boolean equals(Object that)
+      {
+        return this.toString().equals(that.toString());
+      }
+    };
+  }
+
+
+  public static FTSRestClientI getInstance()
+  {
+    if (instance == null)
+    {
+      instance = new UniProtFTSRestClient();
+    }
+    return instance;
+  }
+
+  @Override
+  public String getColumnDataConfigFileName()
+  {
+    return "/fts/uniprot_data_columns.txt";
+  }
+
+}
diff --git a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java
new file mode 100644 (file)
index 0000000..f2fcb76
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+package jalview.fts.service.uniprot;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.core.GFTSPanel;
+import jalview.gui.SequenceFetcher;
+import jalview.util.MessageManager;
+
+import java.util.HashSet;
+
+@SuppressWarnings("serial")
+public class UniprotFTSPanel extends GFTSPanel
+{
+
+  private static String defaultFTSFrameTitle = MessageManager
+          .getString("label.uniprot_sequence_fetcher");
+
+  private String ftsFrameTitle = defaultFTSFrameTitle;
+
+
+
+  public UniprotFTSPanel(SequenceFetcher seqFetcher)
+  {
+    pageLimit = UniProtFTSRestClient.getInstance()
+            .getDefaultResponsePageSize();
+    this.seqFetcher = seqFetcher;
+    this.progressIdicator = (seqFetcher == null) ? null : seqFetcher
+            .getProgressIndicator();
+  }
+
+  @Override
+  public void searchAction(boolean isFreshSearch)
+  {
+    if (isFreshSearch)
+    {
+      offSet = 0;
+    }
+    new Thread()
+  {
+      @Override
+      public void run()
+      {
+        ftsFrameTitle = defaultFTSFrameTitle;
+        reset();
+        if (getTypedText().length() > 0)
+        {
+          setSearchInProgress(true);
+          long startTime = System.currentTimeMillis();
+
+          String searchTarget = ((FTSDataColumnI) cmb_searchTarget
+                  .getSelectedItem()).getAltCode();
+
+          wantedFields = UniProtFTSRestClient.getInstance()
+                  .getAllDefaulDisplayedDataColumns();
+          String searchTerm = decodeSearchTerm(txt_search.getText(),
+                  searchTarget);
+
+          FTSRestRequest request = new FTSRestRequest();
+          request.setFieldToSearchBy(searchTarget);
+          request.setSearchTerm(searchTerm);
+          request.setOffSet(offSet);
+          request.setWantedFields(wantedFields);
+          FTSRestClientI uniProtRestCleint = UniProtFTSRestClient
+                  .getInstance();
+          FTSRestResponse resultList;
+          try
+          {
+            resultList = uniProtRestCleint.executeRequest(request);
+          } catch (Exception e)
+          {
+            e.printStackTrace();
+            setErrorMessage(e.getMessage());
+            checkForErrors();
+            return;
+          }
+
+          if (resultList.getSearchSummary() != null
+                  && resultList.getSearchSummary().size() > 0)
+          {
+            getResultTable().setModel(
+                    FTSRestResponse.getTableModel(request,
+                            resultList.getSearchSummary()));
+            FTSRestResponse.configureTableColumn(getResultTable(),
+                    wantedFields);
+            getResultTable().setVisible(true);
+          }
+
+          long endTime = System.currentTimeMillis();
+          totalResultSetCount = resultList.getNumberOfItemsFound();
+          resultSetCount = resultList.getSearchSummary() == null ? 0
+                  : resultList.getSearchSummary().size();
+          String result = (resultSetCount > 0) ? MessageManager
+                  .getString("label.results") : MessageManager
+                  .getString("label.result");
+          if (isPaginationEnabled() && resultSetCount > 0)
+          {
+            updateSearchFrameTitle(defaultFTSFrameTitle + " - " + result
+                    + " "
+                    + totalNumberformatter.format((Number) (offSet + 1))
+                    + " to "
+                    + totalNumberformatter
+                            .format((Number) (offSet + resultSetCount))
+                    + " of "
+                    + totalNumberformatter
+                            .format((Number) totalResultSetCount)
+                    + " " + " (" + (endTime - startTime) + " milli secs)");
+          }
+          else
+          {
+            updateSearchFrameTitle(defaultFTSFrameTitle + " - "
+                    + resultSetCount + " " + result + " ("
+                    + (endTime - startTime) + " milli secs)");
+          }
+          setSearchInProgress(false);
+          refreshPaginatorState();
+          updateSummaryTableSelections();
+        }
+      }
+    }.start();
+
+  }
+
+  public String decodeSearchTerm(String enteredText, String targetField)
+  {
+    int searchTargetLength = targetField.equalsIgnoreCase("Search All") ? 0
+            : targetField.length() + 1;
+    String searchTarget = targetField.equalsIgnoreCase("Search All") ? ""
+            : targetField + ":";
+    String foundSearchTerms = enteredText;
+    StringBuilder foundSearchTermsBuilder = new StringBuilder();
+    if (enteredText.contains(";"))
+    {
+      String[] searchTerms = enteredText.split(";");
+      for (String searchTerm : searchTerms)
+      {
+        foundSearchTermsBuilder.append(searchTarget).append(searchTerm)
+                .append(" OR ");
+      }
+      int endIndex = foundSearchTermsBuilder.lastIndexOf(" OR ");
+      foundSearchTerms = foundSearchTermsBuilder.toString();
+      if (foundSearchTerms.contains(" OR "))
+      {
+        foundSearchTerms = foundSearchTerms.substring(searchTargetLength,
+                endIndex);
+      }
+    }
+    return foundSearchTerms;
+  }
+
+
+  @Override
+  public boolean isPaginationEnabled()
+  {
+    return true;
+  }
+
+  @Override
+  public void okAction()
+  {
+    disableActionButtons();
+    StringBuilder selectedIds = new StringBuilder();
+    HashSet<String> selectedIdsSet = new HashSet<String>();
+    int primaryKeyColIndex = 0;
+    try
+    {
+      primaryKeyColIndex = getFTSRestClient().getPrimaryKeyColumIndex(
+              wantedFields, false);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    int[] selectedRows = getResultTable().getSelectedRows();
+    for (int summaryRow : selectedRows)
+    {
+      String idStr = getResultTable().getValueAt(summaryRow,
+              primaryKeyColIndex).toString();
+      selectedIdsSet.add(idStr);
+    }
+    selectedIdsSet.addAll(paginatorCart);
+    for (String selectedId : selectedIdsSet)
+    {
+      selectedIds.append(selectedId).append(";");
+    }
+
+    String ids = selectedIds.toString();
+    // System.out.println(">>>>>>>>>>>>>>>> selected Ids: " + ids);
+    seqFetcher.getTextArea().setText(ids);
+    Thread worker = new Thread(seqFetcher);
+    worker.start();
+    delayAndEnableActionButtons();
+  }
+
+  @Override
+  public FTSRestClientI getFTSRestClient()
+  {
+    return UniProtFTSRestClient.getInstance();
+  }
+
+  @Override
+  public String getFTSFrameTitle()
+  {
+    return ftsFrameTitle;
+  }
+
+}
index 21190bd..08122a4 100644 (file)
@@ -74,6 +74,7 @@ import jalview.io.JalviewFileView;
 import jalview.io.JnetAnnotationMaker;
 import jalview.io.NewickFile;
 import jalview.io.TCoffeeScoreFile;
+import jalview.io.gff.SequenceOntologyI;
 import jalview.jbgui.GAlignFrame;
 import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.BuriedColourScheme;
@@ -112,13 +113,14 @@ import java.awt.datatransfer.Clipboard;
 import java.awt.datatransfer.DataFlavor;
 import java.awt.datatransfer.StringSelection;
 import java.awt.datatransfer.Transferable;
-import java.awt.dnd.DnDConstants;
 import java.awt.dnd.DropTargetDragEvent;
 import java.awt.dnd.DropTargetDropEvent;
 import java.awt.dnd.DropTargetEvent;
 import java.awt.dnd.DropTargetListener;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.KeyAdapter;
@@ -466,6 +468,14 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       formatMenu.add(vsel);
     }
+    addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusGained(FocusEvent e)
+      {
+        Desktop.setCurrentAlignFrame(AlignFrame.this);
+      }
+    });
 
   }
 
@@ -911,10 +921,21 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             .setSelected(av.getGlobalColourScheme() instanceof jalview.schemes.RNAHelicesColour);
 
     showProducts.setEnabled(canShowProducts());
+    setGroovyEnabled(Desktop.getGroovyConsole() != null);
 
     updateEditMenuBar();
   }
 
+  /**
+   * Set the enabled state of the 'Run Groovy' option in the Calculate menu
+   * 
+   * @param b
+   */
+  public void setGroovyEnabled(boolean b)
+  {
+    runGroovy.setEnabled(b);
+  }
+
   private IProgressIndicator progressBar;
 
   /*
@@ -1276,13 +1297,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     AlignmentI alignmentToExport = null;
     AlignExportSettingI settings = exportSettings;
     String[] omitHidden = null;
-    int[] alignmentStartEnd = new int[2];
 
     HiddenSequences hiddenSeqs = viewport.getAlignment()
             .getHiddenSequences();
 
     alignmentToExport = viewport.getAlignment();
-    alignmentStartEnd = new int[] { 0, alignmentToExport.getWidth() - 1 };
 
     boolean hasHiddenSeqs = hiddenSeqs.getSize() > 0;
     if (settings == null)
@@ -1297,6 +1316,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       omitHidden = viewport.getViewAsString(false);
     }
 
+    int[] alignmentStartEnd = new int[2];
     if (hasHiddenSeqs && settings.isExportHiddenSequences())
     {
       alignmentToExport = hiddenSeqs.getFullAlignment();
@@ -1304,64 +1324,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     else
     {
       alignmentToExport = viewport.getAlignment();
-      alignmentStartEnd = getStartEnd(alignmentStartEnd, viewport
-              .getColumnSelection().getHiddenColumns());
+      alignmentStartEnd = viewport.getAlignment()
+              .getVisibleStartAndEndIndex(
+                      viewport.getColumnSelection().getHiddenColumns());
     }
     AlignmentExportData ed = new AlignmentExportData(alignmentToExport,
             omitHidden, alignmentStartEnd, settings);
     return ed;
   }
 
-  public static int[] getStartEnd(int[] aligmentStartEnd,
-          List<int[]> hiddenCols)
-  {
-    int startPos = aligmentStartEnd[0];
-    int endPos = aligmentStartEnd[1];
-
-    int[] lowestRange = new int[] { -1, -1 };
-    int[] higestRange = new int[] { -1, -1 };
-
-    for (int[] hiddenCol : hiddenCols)
-    {
-      lowestRange = (hiddenCol[0] <= startPos) ? hiddenCol : lowestRange;
-      higestRange = (hiddenCol[1] >= endPos) ? hiddenCol : higestRange;
-    }
-
-    if (lowestRange[0] == -1 && lowestRange[1] == -1)
-    {
-      startPos = aligmentStartEnd[0];
-    }
-    else
-    {
-      startPos = lowestRange[1] + 1;
-    }
-
-    if (higestRange[0] == -1 && higestRange[1] == -1)
-    {
-      endPos = aligmentStartEnd[1];
-    }
-    else
-    {
-      endPos = higestRange[0] - 1;
-    }
-
-    // System.out.println("Export range : " + startPos + " - " + endPos);
-    return new int[] { startPos, endPos };
-  }
-
-  public static void main(String[] args)
-  {
-    ArrayList<int[]> hiddenCols = new ArrayList<int[]>();
-    hiddenCols.add(new int[] { 0, 0 });
-    hiddenCols.add(new int[] { 6, 9 });
-    hiddenCols.add(new int[] { 11, 12 });
-    hiddenCols.add(new int[] { 33, 33 });
-    hiddenCols.add(new int[] { 50, 50 });
-
-    int[] x = getStartEnd(new int[] { 0, 50 }, hiddenCols);
-    // System.out.println("Export range : " + x[0] + " - " + x[1]);
-  }
-
   /**
    * DOCUMENT ME!
    * 
@@ -3676,8 +3647,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           @Override
           public void mousePressed(MouseEvent evt)
           {
-            if (evt.isControlDown()
-                    || SwingUtilities.isRightMouseButton(evt))
+            if (evt.isPopupTrigger())
             {
               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
 
@@ -4181,7 +4151,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       {
         JMenuItem tm = new JMenuItem();
         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
-        if (sm.isProtein() == !viewport.getAlignment().isNucleotide())
+        if (sm.isDNA() == viewport.getAlignment().isNucleotide()
+                || sm.isProtein() == !viewport.getAlignment()
+                        .isNucleotide())
         {
           String smn = MessageManager.getStringOrReturn(
                   "label.score_model_", sm.getName());
@@ -4748,6 +4720,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
             AlignFrame newFrame = new AlignFrame(al, DEFAULT_WIDTH,
                     DEFAULT_HEIGHT);
+            if (Cache.getDefault("HIDE_INTRONS", true))
+            {
+              newFrame.hideFeatureColumns(SequenceOntologyI.EXON, false);
+            }
             String newtitle = String.format("%s %s %s",
                     MessageManager.getString(dna ? "label.proteins"
                             : "label.nucleotides"), MessageManager
@@ -4875,15 +4851,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
         } catch (Exception e)
         {
-          Cache.log.error(
-                  "Exception when finding crossreferences", e);
+          Cache.log.error("Exception when finding crossreferences", e);
         } catch (OutOfMemoryError e)
         {
           new OOMWarning("whilst fetching crossreferences", e);
         } catch (Throwable e)
         {
-          Cache.log.error("Error when finding crossreferences",
-                  e);
+          Cache.log.error("Error when finding crossreferences", e);
         } finally
         {
           AlignFrame.this.setProgressBar(MessageManager.formatMessage(
@@ -5060,49 +5034,11 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   public void drop(DropTargetDropEvent evt)
   {
     Transferable t = evt.getTransferable();
-    java.util.List files = null;
+    java.util.List<String> files = new ArrayList<String>(), protocols = new ArrayList<String>();
 
     try
     {
-      DataFlavor uriListFlavor = new DataFlavor(
-              "text/uri-list;class=java.lang.String");
-      if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
-      {
-        // Works on Windows and MacOSX
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        files = (java.util.List) t
-                .getTransferData(DataFlavor.javaFileListFlavor);
-      }
-      else if (t.isDataFlavorSupported(uriListFlavor))
-      {
-        // This is used by Unix drag system
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        String data = (String) t.getTransferData(uriListFlavor);
-        files = new java.util.ArrayList(1);
-        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
-                data, "\r\n"); st.hasMoreTokens();)
-        {
-          String s = st.nextToken();
-          if (s.startsWith("#"))
-          {
-            // the line is a comment (as per the RFC 2483)
-            continue;
-          }
-
-          java.net.URI uri = new java.net.URI(s);
-          // check to see if we can handle this kind of URI
-          if (uri.getScheme().toLowerCase().startsWith("http"))
-          {
-            files.add(uri.toString());
-          }
-          else
-          {
-            // otherwise preserve old behaviour: catch all for file objects
-            java.io.File file = new java.io.File(uri);
-            files.add(file.toString());
-          }
-        }
-      }
+      Desktop.transferFromDropTarget(files, protocols, evt, t);
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -5428,7 +5364,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void tabbedPane_mousePressed(MouseEvent e)
   {
-    if (SwingUtilities.isRightMouseButton(e))
+    if (e.isPopupTrigger())
     {
       String msg = MessageManager.getString("label.enter_view_name");
       String reply = JOptionPane.showInternalInputDialog(this, msg, msg,
@@ -5577,8 +5513,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 AlignFrame.this.setMenusForViewport();
               }
             });
-            dbRefFetcher
-                    .fetchDBRefs(false);
+            dbRefFetcher.fetchDBRefs(false);
           }
         }).start();
 
@@ -6136,15 +6071,75 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     try
     {
       Dna dna = new Dna(viewport, viewport.getViewAsVisibleContigs(true));
-
       al = dna.reverseCdna(complement);
       viewport.addAlignment(al, "");
+      addHistoryItem(new EditCommand(
+              MessageManager.getString("label.add_sequences"),
+              Action.PASTE, al.getSequencesArray(), 0, al.getWidth(),
+              viewport.getAlignment()));
     } catch (Exception ex)
     {
       System.err.println(ex.getMessage());
       return;
     }
   }
+
+  /**
+   * Try to run a script in the Groovy console, having first ensured that this
+   * AlignFrame is set as currentAlignFrame in Desktop, to allow the script to
+   * be targeted at this alignment.
+   */
+  @Override
+  protected void runGroovy_actionPerformed()
+  {
+    Desktop.setCurrentAlignFrame(this);
+    groovy.ui.Console console = Desktop.getGroovyConsole();
+    if (console != null)
+    {
+      try
+      {
+        console.runScript();
+      } catch (Exception ex)
+      {
+        System.err.println((ex.toString()));
+        JOptionPane
+                .showInternalMessageDialog(Desktop.desktop, MessageManager
+                        .getString("label.couldnt_run_groovy_script"),
+                        MessageManager
+                                .getString("label.groovy_support_failed"),
+                        JOptionPane.ERROR_MESSAGE);
+      }
+    }
+    else
+    {
+      System.err.println("Can't run Groovy script as console not found");
+    }
+  }
+
+  /**
+   * Hides columns containing (or not containing) a specified feature, provided
+   * that would not leave all columns hidden
+   * 
+   * @param featureType
+   * @param columnsContaining
+   * @return
+   */
+  public boolean hideFeatureColumns(String featureType,
+          boolean columnsContaining)
+  {
+    boolean notForHiding = avc.markColumnsContainingFeatures(
+            columnsContaining, false, false, featureType);
+    if (notForHiding)
+    {
+      if (avc.markColumnsContainingFeatures(!columnsContaining, false,
+              false, featureType))
+      {
+        getViewport().hideSelectedColumns();
+        return true;
+      }
+    }
+    return false;
+  }
 }
 
 class PrintThread extends Thread
index d4300e4..68df498 100755 (executable)
@@ -213,6 +213,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     AlignmentAnnotation[] aa = ap.av.getAlignment()
@@ -325,16 +326,275 @@ public class AnnotationLabels extends JPanel implements MouseListener,
     return true;
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     getSelectedRow(evt.getY() - getScrollOffset());
     oldY = evt.getY();
+    if (!evt.isPopupTrigger())
+    {
+      return;
+    }
+    evt.consume();
+    // handle popup menu event
+    final AlignmentAnnotation[] aa = ap.av.getAlignment()
+            .getAlignmentAnnotation();
+
+    JPopupMenu pop = new JPopupMenu(
+            MessageManager.getString("label.annotations"));
+    JMenuItem item = new JMenuItem(ADDNEW);
+    item.addActionListener(this);
+    pop.add(item);
+    if (selectedRow < 0)
+    {
+      if (hasHiddenRows)
+      { // let the user make everything visible again
+        item = new JMenuItem(SHOWALL);
+        item.addActionListener(this);
+        pop.add(item);
+      }
+      pop.show(this, evt.getX(), evt.getY());
+      return;
+    }
+    item = new JMenuItem(EDITNAME);
+    item.addActionListener(this);
+    pop.add(item);
+    item = new JMenuItem(HIDE);
+    item.addActionListener(this);
+    pop.add(item);
+    // JAL-1264 hide all sequence-specific annotations of this type
+    if (selectedRow < aa.length)
+    {
+      if (aa[selectedRow].sequenceRef != null)
+      {
+        final String label = aa[selectedRow].label;
+        JMenuItem hideType = new JMenuItem();
+        String text = MessageManager.getString("label.hide_all") + " "
+                + label;
+        hideType.setText(text);
+        hideType.addActionListener(new ActionListener()
+        {
+          @Override
+          public void actionPerformed(ActionEvent e)
+          {
+            AlignmentUtils.showOrHideSequenceAnnotations(
+                    ap.av.getAlignment(), Collections.singleton(label),
+                    null, false, false);
+            // for (AlignmentAnnotation ann : ap.av.getAlignment()
+            // .getAlignmentAnnotation())
+            // {
+            // if (ann.sequenceRef != null && ann.label != null
+            // && ann.label.equals(label))
+            // {
+            // ann.visible = false;
+            // }
+            // }
+            refresh();
+          }
+        });
+        pop.add(hideType);
+      }
+    }
+    item = new JMenuItem(DELETE);
+    item.addActionListener(this);
+    pop.add(item);
+    if (hasHiddenRows)
+    {
+      item = new JMenuItem(SHOWALL);
+      item.addActionListener(this);
+      pop.add(item);
+    }
+    item = new JMenuItem(OUTPUT_TEXT);
+    item.addActionListener(this);
+    pop.add(item);
+    // TODO: annotation object should be typed for autocalculated/derived
+    // property methods
+    if (selectedRow < aa.length)
+    {
+      final String label = aa[selectedRow].label;
+      if (!aa[selectedRow].autoCalculated)
+      {
+        if (aa[selectedRow].graph == AlignmentAnnotation.NO_GRAPH)
+        {
+          // display formatting settings for this row.
+          pop.addSeparator();
+          // av and sequencegroup need to implement same interface for
+          item = new JCheckBoxMenuItem(TOGGLE_LABELSCALE,
+                  aa[selectedRow].scaleColLabel);
+          item.addActionListener(this);
+          pop.add(item);
+        }
+      }
+      else if (label.indexOf("Consensus") > -1)
+      {
+        pop.addSeparator();
+        // av and sequencegroup need to implement same interface for
+        final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
+                MessageManager.getString("label.ignore_gaps_consensus"),
+                (aa[selectedRow].groupRef != null) ? aa[selectedRow].groupRef
+                        .getIgnoreGapsConsensus() : ap.av
+                        .isIgnoreGapsConsensus());
+        final AlignmentAnnotation aaa = aa[selectedRow];
+        cbmi.addActionListener(new ActionListener()
+        {
+          @Override
+          public void actionPerformed(ActionEvent e)
+          {
+            if (aaa.groupRef != null)
+            {
+              // TODO: pass on reference to ap so the view can be updated.
+              aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState());
+              ap.getAnnotationPanel().paint(
+                      ap.getAnnotationPanel().getGraphics());
+            }
+            else
+            {
+              ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
+            }
+          }
+        });
+        pop.add(cbmi);
+        // av and sequencegroup need to implement same interface for
+        if (aaa.groupRef != null)
+        {
+          final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
+                  MessageManager.getString("label.show_group_histogram"),
+                  aa[selectedRow].groupRef.isShowConsensusHistogram());
+          chist.addActionListener(new ActionListener()
+          {
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+              // TODO: pass on reference
+              // to ap
+              // so the
+              // view
+              // can be
+              // updated.
+              aaa.groupRef.setShowConsensusHistogram(chist.getState());
+              ap.repaint();
+              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+            }
+          });
+          pop.add(chist);
+          final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem(
+                  MessageManager.getString("label.show_group_logo"),
+                  aa[selectedRow].groupRef.isShowSequenceLogo());
+          cprofl.addActionListener(new ActionListener()
+          {
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+              // TODO: pass on reference
+              // to ap
+              // so the
+              // view
+              // can be
+              // updated.
+              aaa.groupRef.setshowSequenceLogo(cprofl.getState());
+              ap.repaint();
+              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+            }
+          });
+          pop.add(cprofl);
+          final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem(
+                  MessageManager.getString("label.normalise_group_logo"),
+                  aa[selectedRow].groupRef.isNormaliseSequenceLogo());
+          cproflnorm.addActionListener(new ActionListener()
+          {
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+
+              // TODO: pass on reference
+              // to ap
+              // so the
+              // view
+              // can be
+              // updated.
+              aaa.groupRef.setNormaliseSequenceLogo(cproflnorm.getState());
+              // automatically enable logo display if we're clicked
+              aaa.groupRef.setshowSequenceLogo(true);
+              ap.repaint();
+              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+            }
+          });
+          pop.add(cproflnorm);
+        }
+        else
+        {
+          final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
+                  MessageManager.getString("label.show_histogram"),
+                  av.isShowConsensusHistogram());
+          chist.addActionListener(new ActionListener()
+          {
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+              // TODO: pass on reference
+              // to ap
+              // so the
+              // view
+              // can be
+              // updated.
+              av.setShowConsensusHistogram(chist.getState());
+              ap.alignFrame.setMenusForViewport();
+              ap.repaint();
+              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+            }
+          });
+          pop.add(chist);
+          final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem(
+                  MessageManager.getString("label.show_logo"),
+                  av.isShowSequenceLogo());
+          cprof.addActionListener(new ActionListener()
+          {
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+              // TODO: pass on reference
+              // to ap
+              // so the
+              // view
+              // can be
+              // updated.
+              av.setShowSequenceLogo(cprof.getState());
+              ap.alignFrame.setMenusForViewport();
+              ap.repaint();
+              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+            }
+          });
+          pop.add(cprof);
+          final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem(
+                  MessageManager.getString("label.normalise_logo"),
+                  av.isNormaliseSequenceLogo());
+          cprofnorm.addActionListener(new ActionListener()
+          {
+            @Override
+            public void actionPerformed(ActionEvent e)
+            {
+              // TODO: pass on reference
+              // to ap
+              // so the
+              // view
+              // can be
+              // updated.
+              av.setShowSequenceLogo(true);
+              av.setNormaliseSequenceLogo(cprofnorm.getState());
+              ap.alignFrame.setMenusForViewport();
+              ap.repaint();
+              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
+            }
+          });
+          pop.add(cprofnorm);
+        }
+        final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ);
+        consclipbrd.addActionListener(this);
+        pop.add(consclipbrd);
+      }
+    }
+    pop.show(this, evt.getX(), evt.getY());
+
   }
 
   /**
@@ -343,6 +603,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     int start = selectedRow;
@@ -377,6 +638,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
     if (evt.getY() < 10)
@@ -392,6 +654,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseExited(MouseEvent evt)
   {
     if (dragEvent == null)
@@ -407,6 +670,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     dragEvent = evt;
@@ -443,6 +707,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    * @param evt
    *          DOCUMENT ME!
    */
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
     resizePanel = evt.getY() < 10;
@@ -521,17 +786,12 @@ public class AnnotationLabels extends JPanel implements MouseListener,
     }
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param evt
-   *          DOCUMENT ME!
-   */
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
     final AlignmentAnnotation[] aa = ap.av.getAlignment()
             .getAlignmentAnnotation();
-    if (SwingUtilities.isLeftMouseButton(evt))
+    if (!evt.isPopupTrigger() && SwingUtilities.isLeftMouseButton(evt))
     {
       if (selectedRow > -1 && selectedRow < aa.length)
       {
@@ -608,259 +868,8 @@ public class AnnotationLabels extends JPanel implements MouseListener,
 
         }
       }
-    }
-    if (!SwingUtilities.isRightMouseButton(evt))
-    {
-      return;
-    }
-
-    JPopupMenu pop = new JPopupMenu(
-            MessageManager.getString("label.annotations"));
-    JMenuItem item = new JMenuItem(ADDNEW);
-    item.addActionListener(this);
-    pop.add(item);
-    if (selectedRow < 0)
-    {
-      if (hasHiddenRows)
-      { // let the user make everything visible again
-        item = new JMenuItem(SHOWALL);
-        item.addActionListener(this);
-        pop.add(item);
-      }
-      pop.show(this, evt.getX(), evt.getY());
       return;
     }
-    item = new JMenuItem(EDITNAME);
-    item.addActionListener(this);
-    pop.add(item);
-    item = new JMenuItem(HIDE);
-    item.addActionListener(this);
-    pop.add(item);
-    // JAL-1264 hide all sequence-specific annotations of this type
-    if (selectedRow < aa.length)
-    {
-      if (aa[selectedRow].sequenceRef != null)
-      {
-        final String label = aa[selectedRow].label;
-        JMenuItem hideType = new JMenuItem();
-        String text = MessageManager.getString("label.hide_all") + " "
-                + label;
-        hideType.setText(text);
-        hideType.addActionListener(new ActionListener()
-        {
-          @Override
-          public void actionPerformed(ActionEvent e)
-          {
-            AlignmentUtils.showOrHideSequenceAnnotations(
-                    ap.av.getAlignment(), Collections.singleton(label),
-                    null, false, false);
-            // for (AlignmentAnnotation ann : ap.av.getAlignment()
-            // .getAlignmentAnnotation())
-            // {
-            // if (ann.sequenceRef != null && ann.label != null
-            // && ann.label.equals(label))
-            // {
-            // ann.visible = false;
-            // }
-            // }
-            refresh();
-          }
-        });
-        pop.add(hideType);
-      }
-    }
-    item = new JMenuItem(DELETE);
-    item.addActionListener(this);
-    pop.add(item);
-    if (hasHiddenRows)
-    {
-      item = new JMenuItem(SHOWALL);
-      item.addActionListener(this);
-      pop.add(item);
-    }
-    item = new JMenuItem(OUTPUT_TEXT);
-    item.addActionListener(this);
-    pop.add(item);
-    // TODO: annotation object should be typed for autocalculated/derived
-    // property methods
-    if (selectedRow < aa.length)
-    {
-      final String label = aa[selectedRow].label;
-      if (!aa[selectedRow].autoCalculated)
-      {
-        if (aa[selectedRow].graph == AlignmentAnnotation.NO_GRAPH)
-        {
-          // display formatting settings for this row.
-          pop.addSeparator();
-          // av and sequencegroup need to implement same interface for
-          item = new JCheckBoxMenuItem(TOGGLE_LABELSCALE,
-                  aa[selectedRow].scaleColLabel);
-          item.addActionListener(this);
-          pop.add(item);
-        }
-      }
-      else if (label.indexOf("Consensus") > -1)
-      {
-        pop.addSeparator();
-        // av and sequencegroup need to implement same interface for
-        final JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem(
-                MessageManager.getString("label.ignore_gaps_consensus"),
-                (aa[selectedRow].groupRef != null) ? aa[selectedRow].groupRef
-                        .getIgnoreGapsConsensus() : ap.av
-                        .isIgnoreGapsConsensus());
-        final AlignmentAnnotation aaa = aa[selectedRow];
-        cbmi.addActionListener(new ActionListener()
-        {
-          public void actionPerformed(ActionEvent e)
-          {
-            if (aaa.groupRef != null)
-            {
-              // TODO: pass on reference to ap so the view can be updated.
-              aaa.groupRef.setIgnoreGapsConsensus(cbmi.getState());
-              ap.getAnnotationPanel().paint(
-                      ap.getAnnotationPanel().getGraphics());
-            }
-            else
-            {
-              ap.av.setIgnoreGapsConsensus(cbmi.getState(), ap);
-            }
-          }
-        });
-        pop.add(cbmi);
-        // av and sequencegroup need to implement same interface for
-        if (aaa.groupRef != null)
-        {
-          final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
-                  MessageManager.getString("label.show_group_histogram"),
-                  aa[selectedRow].groupRef.isShowConsensusHistogram());
-          chist.addActionListener(new ActionListener()
-          {
-            public void actionPerformed(ActionEvent e)
-            {
-              // TODO: pass on reference
-              // to ap
-              // so the
-              // view
-              // can be
-              // updated.
-              aaa.groupRef.setShowConsensusHistogram(chist.getState());
-              ap.repaint();
-              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
-            }
-          });
-          pop.add(chist);
-          final JCheckBoxMenuItem cprofl = new JCheckBoxMenuItem(
-                  MessageManager.getString("label.show_group_logo"),
-                  aa[selectedRow].groupRef.isShowSequenceLogo());
-          cprofl.addActionListener(new ActionListener()
-          {
-            public void actionPerformed(ActionEvent e)
-            {
-              // TODO: pass on reference
-              // to ap
-              // so the
-              // view
-              // can be
-              // updated.
-              aaa.groupRef.setshowSequenceLogo(cprofl.getState());
-              ap.repaint();
-              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
-            }
-          });
-          pop.add(cprofl);
-          final JCheckBoxMenuItem cproflnorm = new JCheckBoxMenuItem(
-                  MessageManager.getString("label.normalise_group_logo"),
-                  aa[selectedRow].groupRef.isNormaliseSequenceLogo());
-          cproflnorm.addActionListener(new ActionListener()
-          {
-            public void actionPerformed(ActionEvent e)
-            {
-
-              // TODO: pass on reference
-              // to ap
-              // so the
-              // view
-              // can be
-              // updated.
-              aaa.groupRef.setNormaliseSequenceLogo(cproflnorm.getState());
-              // automatically enable logo display if we're clicked
-              aaa.groupRef.setshowSequenceLogo(true);
-              ap.repaint();
-              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
-            }
-          });
-          pop.add(cproflnorm);
-        }
-        else
-        {
-          final JCheckBoxMenuItem chist = new JCheckBoxMenuItem(
-                  MessageManager.getString("label.show_histogram"),
-                  av.isShowConsensusHistogram());
-          chist.addActionListener(new ActionListener()
-          {
-            public void actionPerformed(ActionEvent e)
-            {
-              // TODO: pass on reference
-              // to ap
-              // so the
-              // view
-              // can be
-              // updated.
-              av.setShowConsensusHistogram(chist.getState());
-              ap.alignFrame.setMenusForViewport();
-              ap.repaint();
-              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
-            }
-          });
-          pop.add(chist);
-          final JCheckBoxMenuItem cprof = new JCheckBoxMenuItem(
-                  MessageManager.getString("label.show_logo"),
-                  av.isShowSequenceLogo());
-          cprof.addActionListener(new ActionListener()
-          {
-            public void actionPerformed(ActionEvent e)
-            {
-              // TODO: pass on reference
-              // to ap
-              // so the
-              // view
-              // can be
-              // updated.
-              av.setShowSequenceLogo(cprof.getState());
-              ap.alignFrame.setMenusForViewport();
-              ap.repaint();
-              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
-            }
-          });
-          pop.add(cprof);
-          final JCheckBoxMenuItem cprofnorm = new JCheckBoxMenuItem(
-                  MessageManager.getString("label.normalise_logo"),
-                  av.isNormaliseSequenceLogo());
-          cprofnorm.addActionListener(new ActionListener()
-          {
-            public void actionPerformed(ActionEvent e)
-            {
-              // TODO: pass on reference
-              // to ap
-              // so the
-              // view
-              // can be
-              // updated.
-              av.setShowSequenceLogo(true);
-              av.setNormaliseSequenceLogo(cprofnorm.getState());
-              ap.alignFrame.setMenusForViewport();
-              ap.repaint();
-              // ap.annotationPanel.paint(ap.annotationPanel.getGraphics());
-            }
-          });
-          pop.add(cprofnorm);
-        }
-        final JMenuItem consclipbrd = new JMenuItem(COPYCONS_SEQ);
-        consclipbrd.addActionListener(this);
-        pop.add(consclipbrd);
-      }
-    }
-    pop.show(this, evt.getX(), evt.getY());
   }
 
   /**
@@ -893,7 +902,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
     List<int[]> hiddenCols = av.getColumnSelection().getHiddenColumns();
     if (hiddenCols != null)
     {
-      alignmentStartEnd = AlignFrame.getStartEnd(alignmentStartEnd,
+      alignmentStartEnd = av.getAlignment().getVisibleStartAndEndIndex(
               hiddenCols);
     }
     String output = new FormatAdapter().formatSequences("Fasta", seqs,
@@ -926,6 +935,7 @@ public class AnnotationLabels extends JPanel implements MouseListener,
    * @param g1
    *          DOCUMENT ME!
    */
+  @Override
   public void paintComponent(Graphics g)
   {
 
index a70d4b9..bb311ef 100755 (executable)
@@ -54,7 +54,6 @@ import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.Scrollable;
-import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
 /**
@@ -511,7 +510,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       }
     }
 
-    if (SwingUtilities.isRightMouseButton(evt) && activeRow != -1)
+    if (evt.isPopupTrigger() && activeRow != -1)
     {
       if (av.getColumnSelection() == null)
       {
index 7d5d8d7..65d8670 100644 (file)
@@ -100,6 +100,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
     });
     SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         textarea.requestFocus();
@@ -143,6 +144,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
     textarea.setText(text);
   }
 
+  @Override
   public void save_actionPerformed(ActionEvent e)
   {
     JalviewFileChooser chooser = new JalviewFileChooser(
@@ -173,6 +175,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
     }
   }
 
+  @Override
   public void toggleHtml_actionPerformed(ActionEvent e)
   {
     String txt = textarea.getText();
@@ -187,6 +190,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void copyItem_actionPerformed(ActionEvent e)
   {
     Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
@@ -210,6 +214,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void cancel_actionPerformed(ActionEvent e)
   {
     try
@@ -220,9 +225,10 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
     }
   }
 
+  @Override
   public void textarea_mousePressed(MouseEvent e)
   {
-    if (SwingUtilities.isRightMouseButton(e))
+    if (e.isPopupTrigger())
     {
       JPopupMenu popup = new JPopupMenu(
               MessageManager.getString("action.edit"));
@@ -230,6 +236,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer
               MessageManager.getString("action.copy"));
       item.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           copyItem_actionPerformed(e);
index 76f1041..1161340 100644 (file)
@@ -358,7 +358,7 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
   @Override
   public void textarea_mousePressed(MouseEvent e)
   {
-    if (SwingUtilities.isRightMouseButton(e))
+    if (e.isPopupTrigger())
     {
       JPopupMenu popup = new JPopupMenu(
               MessageManager.getString("action.edit"));
index 53e3c8d..b176672 100644 (file)
@@ -81,6 +81,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     ListSelectionModel rowSM = table.getSelectionModel();
     rowSM.addListSelectionListener(new ListSelectionListener()
     {
+      @Override
       public void valueChanged(ListSelectionEvent e)
       {
         ListSelectionModel lsm = (ListSelectionModel) e.getSource();
@@ -94,10 +95,10 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     table.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mouseClicked(MouseEvent evt)
       {
-        if (evt.getClickCount() == 2
-                || SwingUtilities.isRightMouseButton(evt))
+        if (evt.getClickCount() == 2 || evt.isPopupTrigger())
         {
           editRemoveLocalSource(evt);
         }
@@ -119,6 +120,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     this(null);
   }
 
+  @Override
   public void paintComponent(java.awt.Graphics g)
   {
     if (sourceRegistry == null)
@@ -145,6 +147,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     javax.swing.SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         TableSorter sorter = (TableSorter) table.getModel();
@@ -282,6 +285,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     fullDetails.setText(text.toString());
     javax.swing.SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         fullDetailsScrollpane.getVerticalScrollBar().setValue(0);
@@ -289,6 +293,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     });
   }
 
+  @Override
   public void run()
   {
     loadingDasSources = true;
@@ -358,6 +363,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     return selected;
   }
 
+  @Override
   public void refresh_actionPerformed(ActionEvent e)
   {
     saveProperties(jalview.bin.Cache.applicationProperties);
@@ -411,6 +417,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     javax.swing.SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         filter1.setSelectedIndex(0);
@@ -420,6 +427,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     });
   }
 
+  @Override
   public void amendLocal(boolean newSource)
   {
     String url = "http://localhost:8080/", nickname = "";
@@ -503,6 +511,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     SwingUtilities.invokeLater(new Runnable()
     {
+      @Override
       public void run()
       {
         scrollPane.getVerticalScrollBar().setValue(
@@ -571,6 +580,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
       refreshTableData(data);
       SwingUtilities.invokeLater(new Runnable()
       {
+        @Override
         public void run()
         {
           scrollPane.getVerticalScrollBar().setValue(
@@ -582,6 +592,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     }
   }
 
+  @Override
   public void valueChanged(ListSelectionEvent evt)
   {
     // Called when the MainTable selection changes
@@ -691,6 +702,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     }
   }
 
+  @Override
   public void reset_actionPerformed(ActionEvent e)
   {
     registryURL.setText(sourceRegistry.getDasRegistryURL());
@@ -743,21 +755,25 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     private Object[][] data;
 
+    @Override
     public int getColumnCount()
     {
       return columnNames.length;
     }
 
+    @Override
     public int getRowCount()
     {
       return data.length;
     }
 
+    @Override
     public String getColumnName(int col)
     {
       return columnNames[col];
     }
 
+    @Override
     public Object getValueAt(int row, int col)
     {
       return data[row][col];
@@ -768,6 +784,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
      * each cell. If we didn't implement this method, then the last column would
      * contain text ("true"/"false"), rather than a check box.
      */
+    @Override
     public Class getColumnClass(int c)
     {
       return getValueAt(0, c).getClass();
@@ -776,6 +793,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     /*
      * Don't need to implement this method unless your table's editable.
      */
+    @Override
     public boolean isCellEditable(int row, int col)
     {
       // Note that the data/cell address is constant,
@@ -787,6 +805,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
     /*
      * Don't need to implement this method unless your table's data can change.
      */
+    @Override
     public void setValueAt(Object value, int row, int col)
     {
       data[row][col] = value;
@@ -812,6 +831,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements
 
     Thread thr = new Thread(new Runnable()
     {
+      @Override
       public void run()
       {
         // this actually initialises the das source list
index 27f60ea..698b164 100644 (file)
@@ -47,6 +47,7 @@ import java.awt.GridLayout;
 import java.awt.Point;
 import java.awt.Rectangle;
 import java.awt.Toolkit;
+import java.awt.Window;
 import java.awt.datatransfer.Clipboard;
 import java.awt.datatransfer.ClipboardOwner;
 import java.awt.datatransfer.DataFlavor;
@@ -71,7 +72,6 @@ import java.beans.PropertyVetoException;
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
-import java.lang.reflect.Constructor;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Hashtable;
@@ -312,7 +312,20 @@ public class Desktop extends jalview.jbgui.GDesktop implements
      */
     instance = this;
     doVamsasClientCheck();
-    doGroovyCheck();
+
+    groovyShell = new JMenuItem();
+    groovyShell.setText(MessageManager.getString("label.groovy_console"));
+    groovyShell.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        groovyShell_actionPerformed();
+      }
+    });
+    toolsMenu.add(groovyShell);
+    groovyShell.setVisible(true);
+
     doConfigureStructurePrefs();
     setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
@@ -383,7 +396,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements
       @Override
       public void mousePressed(MouseEvent evt)
       {
-        if (SwingUtilities.isRightMouseButton(evt))
+        if (evt.isPopupTrigger())
         {
           showPasteMenu(evt.getX(), evt.getY());
         }
@@ -939,53 +952,15 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   {
     boolean success = true;
     Transferable t = evt.getTransferable();
-    java.util.List files = null;
-    java.util.List protocols = null;
+    java.util.List<String> files = new ArrayList<String>();
+    java.util.List<String> protocols = new ArrayList<String>();
 
     try
     {
-      DataFlavor uriListFlavor = new DataFlavor(
-              "text/uri-list;class=java.lang.String");
-      if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
-      {
-        // Works on Windows and MacOSX
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        files = (java.util.List) t
-                .getTransferData(DataFlavor.javaFileListFlavor);
-      }
-      else if (t.isDataFlavorSupported(uriListFlavor))
-      {
-        // This is used by Unix drag system
-        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-        String data = (String) t.getTransferData(uriListFlavor);
-        files = new java.util.ArrayList(1);
-        protocols = new java.util.ArrayList(1);
-        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
-                data, "\r\n"); st.hasMoreTokens();)
-        {
-          String s = st.nextToken();
-          if (s.startsWith("#"))
-          {
-            // the line is a comment (as per the RFC 2483)
-            continue;
-          }
-          java.net.URI uri = new java.net.URI(s);
-          if (uri.getScheme().toLowerCase().startsWith("http"))
-          {
-            protocols.add(FormatAdapter.URL);
-            files.add(uri.toString());
-          }
-          else
-          {
-            // otherwise preserve old behaviour: catch all for file objects
-            java.io.File file = new java.io.File(uri);
-            protocols.add(FormatAdapter.FILE);
-            files.add(file.toString());
-          }
-        }
-      }
+      Desktop.transferFromDropTarget(files, protocols, evt, t);
     } catch (Exception e)
     {
+      e.printStackTrace();
       success = false;
     }
 
@@ -2386,25 +2361,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
   protected JMenuItem groovyShell;
 
-  public void doGroovyCheck()
-  {
-    if (jalview.bin.Cache.groovyJarsPresent())
-    {
-      groovyShell = new JMenuItem();
-      groovyShell.setText(MessageManager.getString("label.groovy_console"));
-      groovyShell.addActionListener(new ActionListener()
-      {
-        @Override
-        public void actionPerformed(ActionEvent e)
-        {
-          groovyShell_actionPerformed();
-        }
-      });
-      toolsMenu.add(groovyShell);
-      groovyShell.setVisible(true);
-    }
-  }
-
   /**
    * Accessor method to quickly get all the AlignmentFrames loaded.
    * 
@@ -2492,24 +2448,9 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   public void groovyShell_actionPerformed()
   {
-    // use reflection to avoid creating compilation dependency.
-    if (!jalview.bin.Cache.groovyJarsPresent())
-    {
-      throw new Error(
-              MessageManager
-                      .getString("error.implementation_error_cannot_create_groovyshell"));
-    }
     try
     {
-      Class<?> gcClass = Desktop.class.getClassLoader().loadClass(
-              "groovy.ui.Console");
-      Constructor<?> gccons = gcClass.getConstructor();
-      java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
-              new Class[] { String.class, Object.class });
-      java.lang.reflect.Method run = gcClass.getMethod("run");
-      Object gc = gccons.newInstance();
-      setvar.invoke(gc, new Object[] { "Jalview", this });
-      run.invoke(gc);
+      openGroovyConsole();
     } catch (Exception ex)
     {
       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
@@ -2522,6 +2463,65 @@ public class Desktop extends jalview.jbgui.GDesktop implements
   }
 
   /**
+   * Open the Groovy console
+   */
+  void openGroovyConsole()
+  {
+    groovyConsole = new groovy.ui.Console();
+
+    /*
+     * bind groovy variable 'Jalview' to the Desktop object
+     */
+    groovyConsole.setVariable("Jalview", this);
+
+    /*
+     * start the console
+     */
+    groovyConsole.run();
+
+    /*
+     * Allow only one console at a time, so that the AlignFrame menu option
+     * 'Calculate | Run Groovy script' is unambiguous.
+     * Disable 'new console', and enable 'Run script', when the console is 
+     * opened, and the reverse when it is closed
+     */
+    Window window = (Window) groovyConsole.getFrame();
+    window.addWindowListener(new WindowAdapter()
+    {
+      @Override
+      public void windowClosed(WindowEvent e)
+      {
+        groovyShell.setEnabled(true);
+        enableExecuteGroovy(false);
+      }
+    });
+
+    /*
+     * if we got this far, enable 'Run Groovy' in AlignFrame menus
+     * and disable opening a second console
+     */
+    groovyShell.setEnabled(false);
+    enableExecuteGroovy(true);
+  }
+
+  /**
+   * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
+   * 
+   * @param enabled
+   */
+  public void enableExecuteGroovy(boolean enabled)
+  {
+    AlignFrame[] alignFrames = getAlignFrames();
+    if (alignFrames != null)
+    {
+      for (AlignFrame af : alignFrames)
+      {
+        af.setGroovyEnabled(enabled);
+      }
+    }
+  }
+
+  /**
    * Progress bars managed by the IProgressIndicator method.
    */
   private Hashtable<Long, JPanel> progressBars;
@@ -2917,6 +2917,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   private java.util.concurrent.Semaphore block = new Semaphore(0);
 
+  private static groovy.ui.Console groovyConsole;
+
   /**
    * add another dialog thread to the queue
    * 
@@ -3145,4 +3147,107 @@ public class Desktop extends jalview.jbgui.GDesktop implements
     Desktop.currentAlignFrame = currentAlignFrame;
   }
 
+  public static groovy.ui.Console getGroovyConsole()
+  {
+    return groovyConsole;
+  }
+
+  public static void transferFromDropTarget(List<String> files,
+          List<String> protocols, DropTargetDropEvent evt, Transferable t)
+          throws Exception
+  {
+
+    DataFlavor uriListFlavor = new DataFlavor(
+            "text/uri-list;class=java.lang.String");
+    if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
+    {
+      // Works on Windows and MacOSX
+      Cache.log.debug("Drop handled as javaFileListFlavor");
+      evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
+      for (Object file : (List) t
+              .getTransferData(DataFlavor.javaFileListFlavor))
+      {
+        files.add(((File)file).toString());
+        protocols.add(FormatAdapter.FILE);
+      }
+    }
+    else
+    {
+      // Unix like behaviour
+      boolean added = false;
+      String data = null;
+      if (t.isDataFlavorSupported(uriListFlavor))
+      {
+        Cache.log.debug("Drop handled as uriListFlavor");
+        // This is used by Unix drag system
+        evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
+        data = (String) t.getTransferData(uriListFlavor);
+      }
+      if (data == null)
+      {
+        // fallback to text: workaround - on OSX where there's a JVM bug
+        Cache.log.debug("standard URIListFlavor failed. Trying text");
+        // try text fallback
+        data = (String) t.getTransferData(new DataFlavor(
+                "text/plain;class=java.lang.String"));
+        if (Cache.log.isDebugEnabled())
+        {
+          Cache.log.debug("fallback returned " + data);
+        }
+      }
+      while (protocols.size() < files.size())
+      {
+        Cache.log.debug("Adding missing FILE protocol for "
+                + files.get(protocols.size()));
+        protocols.add(FormatAdapter.FILE);
+      }
+      for (java.util.StringTokenizer st = new java.util.StringTokenizer(
+              data, "\r\n"); st.hasMoreTokens();)
+      {
+        added = true;
+        String s = st.nextToken();
+        if (s.startsWith("#"))
+        {
+          // the line is a comment (as per the RFC 2483)
+          continue;
+        }
+        java.net.URI uri = new java.net.URI(s);
+        if (uri.getScheme().toLowerCase().startsWith("http"))
+        {
+          protocols.add(FormatAdapter.URL);
+          files.add(uri.toString());
+        }
+        else
+        {
+          // otherwise preserve old behaviour: catch all for file objects
+          java.io.File file = new java.io.File(uri);
+          protocols.add(FormatAdapter.FILE);
+          files.add(file.toString());
+        }
+      }
+      if (Cache.log.isDebugEnabled())
+      {
+        if (data == null || !added)
+        {
+          Cache.log
+                  .debug("Couldn't resolve drop data. Here are the supported flavors:");
+          for (DataFlavor fl : t.getTransferDataFlavors())
+          {
+            Cache.log.debug("Supported transfer dataflavor: "
+                    + fl.toString());
+            evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
+            Object df = t.getTransferData(fl);
+            if (df != null)
+            {
+              Cache.log.debug("Retrieves: " + df);
+            }
+            else
+            {
+              Cache.log.debug("Retrieved nothing");
+            }
+          }
+        }
+      }
+    }
+  }
 }
index 6ca0769..09204d1 100644 (file)
@@ -28,7 +28,9 @@ import jalview.gui.Help.HelpId;
 import jalview.io.JalviewFileChooser;
 import jalview.schemes.AnnotationColourGradient;
 import jalview.schemes.GraduatedColor;
+import jalview.util.Format;
 import jalview.util.MessageManager;
+import jalview.util.QuickSort;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.dbsources.das.api.jalviewSourceI;
 
@@ -58,6 +60,7 @@ import java.util.Arrays;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.Vector;
 
@@ -169,7 +172,7 @@ public class FeatureSettings extends JPanel implements
       public void mousePressed(MouseEvent evt)
       {
         selectedRow = table.rowAtPoint(evt.getPoint());
-        if (SwingUtilities.isRightMouseButton(evt))
+        if (evt.isPopupTrigger())
         {
           popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
                   table.getValueAt(selectedRow, 1), fr.getMinMax(),
@@ -277,7 +280,8 @@ public class FeatureSettings extends JPanel implements
   }
 
   protected void popupSort(final int selectedRow, final String type,
-          final Object typeCol, final Hashtable minmax, int x, int y)
+          final Object typeCol, final Map<String, float[][]> minmax, int x,
+          int y)
   {
     JPopupMenu men = new JPopupMenu(MessageManager.formatMessage(
             "label.settings_for_param", new String[] { type }));
@@ -312,7 +316,7 @@ public class FeatureSettings extends JPanel implements
     men.add(dens);
     if (minmax != null)
     {
-      final Object typeMinMax = minmax.get(type);
+      final float[][] typeMinMax = minmax.get(type);
       /*
        * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
        * this is broken at the moment and isn't that useful anyway!
@@ -327,7 +331,7 @@ public class FeatureSettings extends JPanel implements
        * 
        * men.add(chb);
        */
-      if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
+      if (typeMinMax != null && typeMinMax[0] != null)
       {
         // if (table.getValueAt(row, column));
         // graduated colourschemes for those where minmax exists for the
@@ -392,7 +396,6 @@ public class FeatureSettings extends JPanel implements
             MessageManager.getString("label.select_columns_containing"));
     selCols.addActionListener(new ActionListener()
     {
-
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
@@ -404,7 +407,6 @@ public class FeatureSettings extends JPanel implements
             MessageManager.getString("label.select_columns_not_containing"));
     clearCols.addActionListener(new ActionListener()
     {
-
       @Override
       public void actionPerformed(ActionEvent arg0)
       {
@@ -412,8 +414,30 @@ public class FeatureSettings extends JPanel implements
                 false, type);
       }
     });
+    JMenuItem hideCols = new JMenuItem(
+            MessageManager.getString("label.hide_columns_containing"));
+    hideCols.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent arg0)
+      {
+        fr.ap.alignFrame.hideFeatureColumns(type, true);
+      }
+    });
+    JMenuItem hideOtherCols = new JMenuItem(
+            MessageManager.getString("label.hide_columns_not_containing"));
+    hideOtherCols.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent arg0)
+      {
+        fr.ap.alignFrame.hideFeatureColumns(type, false);
+      }
+    });
     men.add(selCols);
     men.add(clearCols);
+    men.add(hideCols);
+    men.add(hideOtherCols);
     men.show(table, x, y);
   }
 
@@ -808,7 +832,7 @@ public class FeatureSettings extends JPanel implements
   void save()
   {
     JalviewFileChooser chooser = new JalviewFileChooser(
-            jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
+            Cache.getProperty("LAST_DIRECTORY"),
             new String[] { "fc" },
             new String[] { "Sequence Feature Colours" },
             "Sequence Feature Colours");
@@ -829,34 +853,35 @@ public class FeatureSettings extends JPanel implements
         PrintWriter out = new PrintWriter(new OutputStreamWriter(
                 new FileOutputStream(choice), "UTF-8"));
 
-        Set fr_colours = fr.getAllFeatureColours();
-        Iterator e = fr_colours.iterator();
+        Set<String> fr_colours = fr.getAllFeatureColours();
+        Iterator<String> e = fr_colours.iterator();
         float[] sortOrder = new float[fr_colours.size()];
         String[] sortTypes = new String[fr_colours.size()];
         int i = 0;
         while (e.hasNext())
         {
-          sortTypes[i] = e.next().toString();
+          sortTypes[i] = e.next();
           sortOrder[i] = fr.getOrder(sortTypes[i]);
           i++;
         }
-        jalview.util.QuickSort.sort(sortOrder, sortTypes);
+        QuickSort.sort(sortOrder, sortTypes);
         sortOrder = null;
         Object fcol;
         GraduatedColor gcol;
-        for (i = 0; i < sortTypes.length; i++)
+        for (String featureType : sortTypes)
         {
           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
-          col.setName(sortTypes[i]);
-          col.setRGB(jalview.util.Format.getHexString(fr.getColour(col
-                  .getName())));
-          fcol = fr.getFeatureStyle(sortTypes[i]);
+          col.setName(featureType);
+          fcol = fr.getFeatureStyle(featureType);
+          Color colour = fcol instanceof Color ? (Color) fcol
+                  : ((GraduatedColor) fcol).getMaxColor();
+          col.setRGB(Format.getHexString(colour));
           if (fcol instanceof GraduatedColor)
           {
             gcol = (GraduatedColor) fcol;
             col.setMin(gcol.getMin());
             col.setMax(gcol.getMax());
-            col.setMinRGB(jalview.util.Format.getHexString(gcol
+            col.setMinRGB(Format.getHexString(gcol
                     .getMinColor()));
             col.setAutoScale(gcol.isAutoScale());
             col.setThreshold(gcol.getThresh());
@@ -1301,7 +1326,7 @@ public class FeatureSettings extends JPanel implements
    * 
    * @return vector of selected das source nicknames
    */
-  public Vector getSelectedSources()
+  public Vector<jalviewSourceI> getSelectedSources()
   {
     return dassourceBrowser.getSelectedSources();
   }
index c89a9d5..37be8bc 100755 (executable)
@@ -204,6 +204,7 @@ public class IdCanvas extends JPanel
    * @param g
    *          DOCUMENT ME!
    */
+  @Override
   public void paintComponent(Graphics g)
   {
     g.setColor(Color.white);
@@ -479,7 +480,7 @@ public class IdCanvas extends JPanel
     Font bold = new Font(av.getFont().getName(), Font.BOLD, av.getFont()
             .getSize());
 
-    if (av.isHiddenRepSequence(seq))
+    if (av.isReferenceSeq(seq) || av.isHiddenRepSequence(seq))
     {
       gg.setFont(bold);
     }
index bbffbab..c84505b 100755 (executable)
@@ -193,6 +193,8 @@ public class IdPanel extends JPanel implements MouseListener,
      */
     if (e.getClickCount() < 2 || SwingUtilities.isRightMouseButton(e))
     {
+      // reinstate isRightMouseButton check to ignore mouse-related popup events
+      // note - this does nothing on default MacBookPro force-trackpad config!
       return;
     }
 
@@ -316,29 +318,33 @@ public class IdPanel extends JPanel implements MouseListener,
 
     int seq = alignPanel.getSeqPanel().findSeq(e);
 
-    if (SwingUtilities.isRightMouseButton(e))
+    if (e.isPopupTrigger())
     {
       Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq);
       // build a new links menu based on the current links + any non-positional
       // features
-      Vector nlinks = new Vector(Preferences.sequenceURLLinks);
-      SequenceFeature sf[] = sq == null ? null : sq.getSequenceFeatures();
-      for (int sl = 0; sf != null && sl < sf.length; sl++)
+      Vector<String> nlinks = new Vector<String>(
+              Preferences.sequenceURLLinks);
+      SequenceFeature sfs[] = sq == null ? null : sq.getSequenceFeatures();
+      if (sfs != null)
       {
-        if (sf[sl].begin == sf[sl].end && sf[sl].begin == 0)
+        for (SequenceFeature sf : sfs)
         {
-          if (sf[sl].links != null && sf[sl].links.size() > 0)
+          if (sf.begin == sf.end && sf.begin == 0)
           {
-            for (int l = 0, lSize = sf[sl].links.size(); l < lSize; l++)
+            if (sf.links != null && sf.links.size() > 0)
             {
-              nlinks.addElement(sf[sl].links.elementAt(l));
+              for (int l = 0, lSize = sf.links.size(); l < lSize; l++)
+              {
+                nlinks.addElement(sf.links.elementAt(l));
+              }
             }
           }
         }
       }
 
-      jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(alignPanel, sq,
-              nlinks, new Vector(Preferences.getGroupURLLinks()));
+      PopupMenu pop = new PopupMenu(alignPanel, sq, nlinks,
+              Preferences.getGroupURLLinks());
       pop.show(this, e.getX(), e.getY());
 
       return;
index 2799a7e..a3604d6 100644 (file)
@@ -79,6 +79,7 @@ import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.util.StringUtils;
 import jalview.util.jarInputStreamProvider;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.viewmodel.seqfeatures.FeatureRendererSettings;
@@ -90,6 +91,7 @@ import jalview.ws.params.ArgumentI;
 import jalview.ws.params.AutoCalcSetting;
 import jalview.ws.params.WsParamSetI;
 
+import java.awt.Color;
 import java.awt.Rectangle;
 import java.io.BufferedReader;
 import java.io.DataInputStream;
@@ -116,7 +118,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.StringTokenizer;
 import java.util.Vector;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
@@ -1194,17 +1195,17 @@ public class Jalview2XML
                 .getFeatureRenderer().getRenderOrder()
                 .toArray(new String[0]);
 
-        Vector settingsAdded = new Vector();
+        Vector<String> settingsAdded = new Vector<String>();
         Object gstyle = null;
         GraduatedColor gcol = null;
         if (renderOrder != null)
         {
-          for (int ro = 0; ro < renderOrder.length; ro++)
+          for (String featureType : renderOrder)
           {
             gstyle = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                    .getFeatureStyle(renderOrder[ro]);
+                    .getFeatureStyle(featureType);
             Setting setting = new Setting();
-            setting.setType(renderOrder[ro]);
+            setting.setType(featureType);
             if (gstyle instanceof GraduatedColor)
             {
               gcol = (GraduatedColor) gstyle;
@@ -1219,57 +1220,30 @@ public class Jalview2XML
             }
             else
             {
-              setting.setColour(ap.getSeqPanel().seqCanvas
-                      .getFeatureRenderer().getColour(renderOrder[ro])
-                      .getRGB());
+              setting.setColour(((Color) gstyle).getRGB());
             }
 
             setting.setDisplay(av.getFeaturesDisplayed().isVisible(
-                    renderOrder[ro]));
+                    featureType));
             float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                    .getOrder(renderOrder[ro]);
+                    .getOrder(featureType);
             if (rorder > -1)
             {
               setting.setOrder(rorder);
             }
             fs.addSetting(setting);
-            settingsAdded.addElement(renderOrder[ro]);
+            settingsAdded.addElement(featureType);
           }
         }
 
-        // Make sure we save none displayed feature settings
-        Iterator en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                .getFeatureColours().keySet().iterator();
-        while (en.hasNext())
-        {
-          String key = en.next().toString();
-          if (settingsAdded.contains(key))
-          {
-            continue;
-          }
-
-          Setting setting = new Setting();
-          setting.setType(key);
-          setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                  .getColour(key).getRGB());
-
-          setting.setDisplay(false);
-          float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
-                  .getOrder(key);
-          if (rorder > -1)
-          {
-            setting.setOrder(rorder);
-          }
-          fs.addSetting(setting);
-          settingsAdded.addElement(key);
-        }
         // is groups actually supposed to be a map here ?
-        en = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
+        Iterator<String> en = ap.getSeqPanel().seqCanvas
+                .getFeatureRenderer()
                 .getFeatureGroups().iterator();
-        Vector groupsAdded = new Vector();
+        Vector<String> groupsAdded = new Vector<String>();
         while (en.hasNext())
         {
-          String grp = en.next().toString();
+          String grp = en.next();
           if (groupsAdded.contains(grp))
           {
             continue;
@@ -4047,18 +4021,22 @@ public class Jalview2XML
   }
 
   /**
+   * Answers true if 'version' is equal to or later than 'supported', where each
+   * is formatted as major/minor versions like "2.8.3" or "2.3.4b1" for bugfix
+   * changes. Development and test values for 'version' are leniently treated
+   * i.e. answer true.
    * 
    * @param supported
    *          - minimum version we are comparing against
    * @param version
-   *          - version of data being processsed.
-   * @return true if version is development/null or evaluates to the same or
-   *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
+   *          - version of data being processsed
+   * @return
    */
   public static boolean isVersionStringLaterThan(String supported,
           String version)
   {
-    if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
+    if (supported == null || version == null
+            || version.equalsIgnoreCase("DEVELOPMENT BUILD")
             || version.equalsIgnoreCase("Test")
             || version.equalsIgnoreCase("AUTOMATED BUILD"))
     {
@@ -4069,45 +4047,8 @@ public class Jalview2XML
     }
     else
     {
-      StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
-              version, ".");
-      while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
-      {
-        // convert b to decimal to catch bugfix releases within a series
-        String curT = currentV.nextToken().toLowerCase().replace('b', '.');
-        String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
-        try
-        {
-          float supportedVersionToken = Float.parseFloat(curT);
-          float myVersiontoken = Float.parseFloat(fileT);
-          if (supportedVersionToken > myVersiontoken)
-          {
-            // current version is newer than the version that wrote the file
-            return false;
-          }
-          if (supportedVersionToken < myVersiontoken)
-          {
-            // current version is older than the version that wrote the file
-            return true;
-          }
-        } catch (NumberFormatException nfe)
-        {
-          System.err
-                  .println("** WARNING: Version comparison failed for tokens ("
-                          + curT
-                          + ") and ("
-                          + fileT
-                          + ")\n** Current: '"
-                          + supported + "' and Version: '" + version + "'");
-        }
-      }
-      if (currentV.hasMoreElements())
-      {
-        // fileV has no minor version but identical series to current
-        return false;
-      }
+      return StringUtils.compareVersions(version, supported, "b") >= 0;
     }
-    return true;
   }
 
   Vector<JalviewStructureDisplayI> newStructureViewers = null;
index 8a0fe77..801cb1f 100644 (file)
@@ -130,9 +130,9 @@ public class OptsAndParamsPage
       add(enabled, BorderLayout.NORTH);
       for (Object str : opt.getPossibleValues())
       {
-        val.addItem((String) str);
+        val.addItem(str);
       }
-      val.setSelectedItem((String) opt.getValue());
+      val.setSelectedItem(opt.getValue());
       if (opt.getPossibleValues().size() > 1)
       {
         setLayout(new GridLayout(1, 2));
@@ -145,6 +145,7 @@ public class OptsAndParamsPage
       setInitialValue();
     }
 
+    @Override
     public void actionPerformed(ActionEvent e)
     {
       if (e.getSource() != enabled)
@@ -206,32 +207,37 @@ public class OptsAndParamsPage
       return opt;
     }
 
+    @Override
     public void mouseClicked(MouseEvent e)
     {
-      if (javax.swing.SwingUtilities.isRightMouseButton(e))
+      if (e.isPopupTrigger())
       {
         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
       }
     }
 
+    @Override
     public void mouseEntered(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mouseExited(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mousePressed(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mouseReleased(MouseEvent e)
     {
       // TODO Auto-generated method stub
@@ -412,6 +418,7 @@ public class OptsAndParamsPage
         showDesc.addActionListener(new ActionListener()
         {
 
+          @Override
           public void actionPerformed(ActionEvent e)
           {
             descisvisible = !descisvisible;
@@ -451,6 +458,7 @@ public class OptsAndParamsPage
       validate();
     }
 
+    @Override
     public void actionPerformed(ActionEvent e)
     {
       if (adjusting)
@@ -526,45 +534,51 @@ public class OptsAndParamsPage
       lastVal = null;
     }
 
+    @Override
     public void mouseClicked(MouseEvent e)
     {
-      if (javax.swing.SwingUtilities.isRightMouseButton(e))
+      if (e.isPopupTrigger())
       {
         showUrlPopUp(this, finfo.toString(), e.getX(), e.getY());
       }
     }
 
+    @Override
     public void mouseEntered(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mouseExited(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mousePressed(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void mouseReleased(MouseEvent e)
     {
       // TODO Auto-generated method stub
 
     }
 
+    @Override
     public void stateChanged(ChangeEvent e)
     {
       if (!adjusting)
       {
         valueField.setText(""
-                + ((integ) ? ("" + (int) slider.getValue())
-                        : ("" + (float) (slider.getValue() / 1000f))));
+                + ((integ) ? ("" + slider.getValue())
+                        : ("" + slider.getValue() / 1000f)));
         checkIfModified();
       }
 
diff --git a/src/jalview/gui/PDBSearchPanel.java b/src/jalview/gui/PDBSearchPanel.java
deleted file mode 100644 (file)
index 9b94c27..0000000
+++ /dev/null
@@ -1,323 +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.
- */
-
-package jalview.gui;
-
-import jalview.jbgui.GPDBSearchPanel;
-import jalview.jbgui.PDBDocFieldPreferences;
-import jalview.util.MessageManager;
-import jalview.ws.dbsources.PDBRestClient;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-import jalview.ws.uimodel.PDBRestRequest;
-import jalview.ws.uimodel.PDBRestResponse;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-
-import javax.swing.table.DefaultTableModel;
-
-@SuppressWarnings("serial")
-public class PDBSearchPanel extends GPDBSearchPanel
-{
-  private SequenceFetcher seqFetcher;
-
-  private IProgressIndicator progressIdicator;
-
-  private Collection<PDBDocField> wantedFields;
-
-  public PDBSearchPanel(SequenceFetcher seqFetcher)
-  {
-    this.seqFetcher = seqFetcher;
-    this.progressIdicator = (seqFetcher == null) ? null : seqFetcher
-            .getProgressIndicator();
-  }
-
-  /**
-   * Action performed when an input is detected on txt_search field.
-   */
-  @Override
-  public void txt_search_ActionPerformed()
-  {
-    new Thread()
-    {
-      @Override
-      public void run()
-      {
-        lbl_loading.setVisible(false);
-        errorWarning.setLength(0);
-        lbl_warning.setVisible(false);
-        btn_ok.setEnabled(false);
-        boolean allowEmptySequence = false;
-        mainFrame.setTitle(MessageManager
-                .getString("label.pdb_sequence_getcher"));
-        tbl_summary.setModel(new DefaultTableModel());
-        if (txt_search.getText().trim().length() > 0)
-        {
-          lbl_loading.setVisible(true);
-          long startTime = System.currentTimeMillis();
-
-          String searchTarget = ((PDBDocField) cmb_searchTarget
-                  .getSelectedItem()).getCode();
-
-          wantedFields = PDBDocFieldPreferences.getSearchSummaryFields();
-
-          String searchTerm = decodeSearchTerm(txt_search.getText(),
-                  searchTarget);
-
-          PDBRestRequest request = new PDBRestRequest();
-          request.setAllowEmptySeq(allowEmptySequence);
-          request.setResponseSize(100);
-          request.setFieldToSearchBy("(" + searchTarget + ":");
-          request.setSearchTerm(searchTerm + ")");
-          request.setWantedFields(wantedFields);
-          // System.out.println(">>>>>>>>>>>>>> " + request.getQuery());
-          PDBRestClient pdbRestCleint = new PDBRestClient();
-          PDBRestResponse resultList;
-          try
-          {
-            resultList = pdbRestCleint.executeRequest(request);
-          } catch (Exception e)
-          {
-            // e.printStackTrace();
-            errorWarning.append(e.getMessage());
-            checkForErrors();
-            return;
-          }
-
-          if (resultList.getSearchSummary() != null)
-          {
-            tbl_summary.setModel(PDBRestResponse.getTableModel(request,
-                    resultList.getSearchSummary()));
-          }
-
-          PDBRestResponse.configureTableColumn(tbl_summary, wantedFields);
-
-          long endTime = System.currentTimeMillis();
-          int resultSetCount = resultList.getNumberOfItemsFound();
-          String result = (resultSetCount > 1) ? MessageManager
-                  .getString("label.results") : MessageManager
-                  .getString("label.result");
-          mainFrame.setTitle(frameTitle + " - " + resultSetCount + " "
-                  + result + " (" + (endTime - startTime) + " milli secs)");
-          lbl_loading.setVisible(false);
-        }
-      }
-    }.start();
-  }
-
-  public static String decodeSearchTerm(String enteredText,
-          String targetField)
-  {
-    String foundSearchTerms = enteredText;
-    StringBuilder foundSearchTermsBuilder = new StringBuilder();
-    if (enteredText.contains(";"))
-    {
-      String[] searchTerms = enteredText.split(";");
-      for (String searchTerm : searchTerms)
-      {
-        if (searchTerm.contains(":"))
-        {
-          foundSearchTermsBuilder.append(targetField).append(":")
-                  .append(searchTerm.split(":")[0]).append(" OR ");
-        }
-        else
-        {
-          foundSearchTermsBuilder.append(targetField).append(":")
-                  .append(searchTerm).append(" OR ");
-        }
-      }
-      int endIndex = foundSearchTermsBuilder.lastIndexOf(" OR ");
-      foundSearchTerms = foundSearchTermsBuilder.toString();
-      if (foundSearchTerms.contains(" OR "))
-      {
-        foundSearchTerms = foundSearchTerms.substring(
-                targetField.length() + 1, endIndex);
-      }
-    }
-    else if (enteredText.contains(":"))
-    {
-      foundSearchTerms = foundSearchTerms.split(":")[0];
-    }
-    return foundSearchTerms;
-  }
-
-  @Override
-  public void btn_ok_ActionPerformed()
-  {
-    loadSelectedPDBSequencesToAlignment();
-  }
-
-  @Override
-  public void btn_back_ActionPerformed()
-  {
-    mainFrame.dispose();
-    new SequenceFetcher(progressIdicator);
-  }
-
-  @Override
-  public void btn_cancel_ActionPerformed()
-  {
-    mainFrame.dispose();
-  }
-
-  @Override
-  public void transferToSequenceFetcher(String ids)
-  {
-    // mainFrame.dispose();
-    seqFetcher.textArea.setText(ids);
-    Thread worker = new Thread(seqFetcher);
-    worker.start();
-  }
-
-  /**
-   * Add the discovered/selected sequences to a target alignment window
-   */
-  public void loadSelectedPDBSequencesToAlignment()
-  {
-    // mainFrame.dispose();
-    disableActionButtons();
-    StringBuilder selectedIds = new StringBuilder();
-    HashSet<String> selectedIdsSet = new HashSet<String>();
-    int pdbIdCol = PDBRestClient.getPDBIdColumIndex(wantedFields, false);
-    int[] selectedRows = tbl_summary.getSelectedRows();
-    for (int summaryRow : selectedRows)
-    {
-      String pdbIdStr = tbl_summary.getValueAt(summaryRow, pdbIdCol)
-              .toString();
-      String searchTerm = txt_search.getText();
-      selectedIdsSet.add(getPDBIdwithSpecifiedChain(pdbIdStr, searchTerm));
-    }
-
-    for (String selectedId : selectedIdsSet)
-    {
-      selectedIds.append(selectedId).append(";");
-    }
-
-    String ids = selectedIds.toString();
-    // System.out.println(">>>>>>>>>>>>>>>> selected Ids: " + ids);
-    seqFetcher.textArea.setText(ids);
-    Thread worker = new Thread(seqFetcher);
-    worker.start();
-    delayAndEnableActionButtons();
-
-  }
-
-  private void disableActionButtons()
-  {
-    btn_ok.setEnabled(false);
-    btn_back.setEnabled(false);
-    btn_cancel.setEnabled(false);
-  }
-
-  private void delayAndEnableActionButtons()
-  {
-    new Thread()
-    {
-      @Override
-      public void run()
-      {
-        try
-        {
-          Thread.sleep(1500);
-        } catch (InterruptedException e)
-        {
-          e.printStackTrace();
-        }
-        btn_ok.setEnabled(true);
-        btn_back.setEnabled(true);
-        btn_cancel.setEnabled(true);
-      }
-    }.start();
-  }
-
-  public static String getPDBIdwithSpecifiedChain(String pdbId,
-          String searchTerm)
-  {
-    String pdbIdWithChainCode = "";
-    if (searchTerm.contains(";"))
-    {
-      String[] foundTerms = searchTerm.split(";");
-      for (String foundTerm : foundTerms)
-      {
-        if (foundTerm.contains(pdbId))
-        {
-          pdbIdWithChainCode = foundTerm;
-        }
-      }
-    }
-    else if (searchTerm.contains(pdbId))
-    {
-      pdbIdWithChainCode = searchTerm;
-    }
-    else
-    {
-      pdbIdWithChainCode = pdbId;
-    }
-    return pdbIdWithChainCode;
-  }
-
-  /**
-   * Populates search target combo-box options
-   */
-  @Override
-  public void populateCmbSearchTargetOptions()
-  {
-    List<PDBDocField> searchableTargets = new ArrayList<PDBDocField>();
-    searchableTargets.add(PDBDocField.PDB_ID);
-    searchableTargets.add(PDBDocField.PFAM_ACCESSION);
-    searchableTargets.add(PDBDocField.MOLECULE_TYPE);
-    searchableTargets.add(PDBDocField.MOLECULE_NAME);
-    searchableTargets.add(PDBDocField.UNIPROT_ACCESSION);
-    searchableTargets.add(PDBDocField.GENE_NAME);
-    searchableTargets.add(PDBDocField.GENUS);
-    searchableTargets.add(PDBDocField.ALL);
-
-    Collections.sort(searchableTargets, new Comparator<PDBDocField>()
-    {
-      @Override
-      public int compare(PDBDocField o1, PDBDocField o2)
-      {
-        return o1.getName().compareTo(o2.getName());
-      }
-    });
-
-    for (PDBDocField searchTarget : searchableTargets)
-    {
-      cmb_searchTarget.addItem(searchTarget);
-    }
-  }
-
-  public void checkForErrors()
-  {
-    lbl_warning.setVisible(false);
-    if (errorWarning.length() > 0)
-    {
-      lbl_loading.setVisible(false);
-      lbl_warning.setToolTipText(JvSwingUtils.wrapTooltip(true,
-              errorWarning.toString()));
-      lbl_warning.setVisible(true);
-    }
-  }
-}
index ab8c398..4b0ffad 100644 (file)
@@ -24,6 +24,7 @@ import jalview.analysis.AAFrequency;
 import jalview.analysis.AlignmentAnnotationUtils;
 import jalview.analysis.AlignmentUtils;
 import jalview.analysis.Conservation;
+import jalview.bin.Cache;
 import jalview.commands.ChangeCaseCommand;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
@@ -54,14 +55,17 @@ import jalview.schemes.TaylorColourScheme;
 import jalview.schemes.TurnColourScheme;
 import jalview.schemes.UserColourScheme;
 import jalview.schemes.ZappoColourScheme;
+import jalview.util.DBRefUtils;
 import jalview.util.GroupUrlLink;
 import jalview.util.GroupUrlLink.UrlStringTooLongException;
 import jalview.util.MessageManager;
 import jalview.util.UrlLink;
+import jalview.ws.DBRefFetcher;
 
 import java.awt.Color;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Hashtable;
@@ -90,8 +94,6 @@ public class PopupMenu extends JPopupMenu
 {
   private static final String ALL_ANNOTATIONS = "All";
 
-  private static final String COMMA = ",";
-
   JMenu groupMenu = new JMenu();
 
   JMenuItem groupName = new JMenuItem();
@@ -216,7 +218,7 @@ public class PopupMenu extends JPopupMenu
    * @param seq
    *          DOCUMENT ME!
    */
-  public PopupMenu(final AlignmentPanel ap, Sequence seq, Vector links)
+  public PopupMenu(final AlignmentPanel ap, Sequence seq, List<String> links)
   {
     this(ap, seq, links, null);
   }
@@ -229,7 +231,7 @@ public class PopupMenu extends JPopupMenu
    * @param groupLinks
    */
   public PopupMenu(final AlignmentPanel ap, final SequenceI seq,
-          Vector links, Vector groupLinks)
+          List<String> links, List<String> groupLinks)
   {
     // /////////////////////////////////////////////////////////
     // If this is activated from the sequence panel, the user may want to
@@ -608,124 +610,130 @@ public class PopupMenu extends JPopupMenu
 
     if (links != null && links.size() > 0)
     {
+      addFeatureLinks(seq, links);
+    }
+  }
 
-      JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
-      Vector linkset = new Vector();
-      for (int i = 0; i < links.size(); i++)
+  /**
+   * Adds a 'Link' menu item with a sub-menu item for each hyperlink provided.
+   * 
+   * @param seq
+   * @param links
+   */
+  void addFeatureLinks(final SequenceI seq, List<String> links)
+  {
+    JMenu linkMenu = new JMenu(MessageManager.getString("action.link"));
+    List<String> linkset = new ArrayList<String>();
+    for (String link : links)
+    {
+      UrlLink urlLink = null;
+      try
       {
-        String link = links.elementAt(i).toString();
-        UrlLink urlLink = null;
-        try
-        {
-          urlLink = new UrlLink(link);
-        } catch (Exception foo)
-        {
-          jalview.bin.Cache.log.error("Exception for URLLink '" + link
-                  + "'", foo);
-          continue;
-        }
-        ;
-        if (!urlLink.isValid())
+        urlLink = new UrlLink(link);
+      } catch (Exception foo)
+      {
+        Cache.log.error("Exception for URLLink '" + link + "'", foo);
+        continue;
+      }
+      ;
+      if (!urlLink.isValid())
+      {
+        Cache.log.error(urlLink.getInvalidMessage());
+        continue;
+      }
+      final String label = urlLink.getLabel();
+      if (seq != null && urlLink.isDynamic())
+      {
+
+        // collect matching db-refs
+        DBRefEntry[] dbr = DBRefUtils.selectRefs(seq.getDBRefs(),
+                new String[] { urlLink.getTarget() });
+        // collect id string too
+        String id = seq.getName();
+        String descr = seq.getDescription();
+        if (descr != null && descr.length() < 1)
         {
-          jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
-          continue;
+          descr = null;
         }
-        final String label = urlLink.getLabel();
-        if (seq != null && urlLink.isDynamic())
-        {
 
-          // collect matching db-refs
-          DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(
-                  seq.getDBRefs(), new String[] { urlLink.getTarget() });
-          // collect id string too
-          String id = seq.getName();
-          String descr = seq.getDescription();
-          if (descr != null && descr.length() < 1)
-          {
-            descr = null;
-          }
-
-          if (dbr != null)
+        if (dbr != null)
+        {
+          for (int r = 0; r < dbr.length; r++)
           {
-            for (int r = 0; r < dbr.length; r++)
+            if (id != null && dbr[r].getAccessionId().equals(id))
             {
-              if (id != null && dbr[r].getAccessionId().equals(id))
-              {
-                // suppress duplicate link creation for the bare sequence ID
-                // string with this link
-                id = null;
-              }
-              // create Bare ID link for this RUL
-              String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(),
-                      true);
-              if (urls != null)
-              {
-                for (int u = 0; u < urls.length; u += 2)
-                {
-                  if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
-                  {
-                    linkset.addElement(urls[u] + "|" + urls[u + 1]);
-                    addshowLink(linkMenu, label + "|" + urls[u],
-                            urls[u + 1]);
-                  }
-                }
-              }
+              // suppress duplicate link creation for the bare sequence ID
+              // string with this link
+              id = null;
             }
-          }
-          if (id != null)
-          {
-            // create Bare ID link for this RUL
-            String[] urls = urlLink.makeUrls(id, true);
+            // create Bare ID link for this URL
+            String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(), true);
             if (urls != null)
             {
               for (int u = 0; u < urls.length; u += 2)
               {
                 if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
                 {
-                  linkset.addElement(urls[u] + "|" + urls[u + 1]);
-                  addshowLink(linkMenu, label, urls[u + 1]);
+                  linkset.add(urls[u] + "|" + urls[u + 1]);
+                  addshowLink(linkMenu, label + "|" + urls[u], urls[u + 1]);
                 }
               }
             }
           }
-          // Create urls from description but only for URL links which are regex
-          // links
-          if (descr != null && urlLink.getRegexReplace() != null)
+        }
+        if (id != null)
+        {
+          // create Bare ID link for this URL
+          String[] urls = urlLink.makeUrls(id, true);
+          if (urls != null)
           {
-            // create link for this URL from description where regex matches
-            String[] urls = urlLink.makeUrls(descr, true);
-            if (urls != null)
+            for (int u = 0; u < urls.length; u += 2)
             {
-              for (int u = 0; u < urls.length; u += 2)
+              if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
               {
-                if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
-                {
-                  linkset.addElement(urls[u] + "|" + urls[u + 1]);
-                  addshowLink(linkMenu, label, urls[u + 1]);
-                }
+                linkset.add(urls[u] + "|" + urls[u + 1]);
+                addshowLink(linkMenu, label, urls[u + 1]);
               }
             }
           }
         }
-        else
+        // Create urls from description but only for URL links which are regex
+        // links
+        if (descr != null && urlLink.getRegexReplace() != null)
         {
-          if (!linkset.contains(label + "|" + urlLink.getUrl_prefix()))
+          // create link for this URL from description where regex matches
+          String[] urls = urlLink.makeUrls(descr, true);
+          if (urls != null)
           {
-            linkset.addElement(label + "|" + urlLink.getUrl_prefix());
-            // Add a non-dynamic link
-            addshowLink(linkMenu, label, urlLink.getUrl_prefix());
+            for (int u = 0; u < urls.length; u += 2)
+            {
+              if (!linkset.contains(urls[u] + "|" + urls[u + 1]))
+              {
+                linkset.add(urls[u] + "|" + urls[u + 1]);
+                addshowLink(linkMenu, label, urls[u + 1]);
+              }
+            }
           }
         }
       }
-      if (sequence != null)
-      {
-        sequenceMenu.add(linkMenu);
-      }
       else
       {
-        add(linkMenu);
+        if (!linkset.contains(label + "|" + urlLink.getUrl_prefix()))
+        {
+          linkset.add(label + "|" + urlLink.getUrl_prefix());
+          // Add a non-dynamic link
+          addshowLink(linkMenu, label, urlLink.getUrl_prefix());
+        }
       }
     }
+    if (sequence != null)
+    {
+      sequenceMenu.add(linkMenu);
+    }
+    else
+    {
+      add(linkMenu);
+    }
   }
 
   /**
@@ -855,7 +863,7 @@ public class PopupMenu extends JPopupMenu
     showOrHideMenu.add(item);
   }
 
-  private void buildGroupURLMenu(SequenceGroup sg, Vector groupLinks)
+  private void buildGroupURLMenu(SequenceGroup sg, List<String> groupLinks)
   {
 
     // TODO: usability: thread off the generation of group url content so root
@@ -864,19 +872,15 @@ public class PopupMenu extends JPopupMenu
     // ID/regex match URLs
     groupLinksMenu = new JMenu(
             MessageManager.getString("action.group_link"));
+    // three types of url that might be created.
     JMenu[] linkMenus = new JMenu[] { null,
         new JMenu(MessageManager.getString("action.ids")),
         new JMenu(MessageManager.getString("action.sequences")),
-        new JMenu(MessageManager.getString("action.ids_sequences")) }; // three
-                                                                       // types
-                                                                       // of url
-                                                                       // that
-                                                                       // might
-                                                                       // be
-    // created.
+        new JMenu(MessageManager.getString("action.ids_sequences")) };
+
     SequenceI[] seqs = ap.av.getSelectionAsNewSequence();
     String[][] idandseqs = GroupUrlLink.formStrings(seqs);
-    Hashtable commonDbrefs = new Hashtable();
+    Hashtable<String, Object[]> commonDbrefs = new Hashtable<String, Object[]>();
     for (int sq = 0; sq < seqs.length; sq++)
     {
 
@@ -896,7 +900,7 @@ public class PopupMenu extends JPopupMenu
         for (int d = 0; d < dbr.length; d++)
         {
           String src = dbr[d].getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase();
-          Object[] sarray = (Object[]) commonDbrefs.get(src);
+          Object[] sarray = commonDbrefs.get(src);
           if (sarray == null)
           {
             sarray = new Object[2];
@@ -921,30 +925,29 @@ public class PopupMenu extends JPopupMenu
     // now create group links for all distinct ID/sequence sets.
     boolean addMenu = false; // indicates if there are any group links to give
                              // to user
-    for (int i = 0; i < groupLinks.size(); i++)
+    for (String link : groupLinks)
     {
-      String link = groupLinks.elementAt(i).toString();
       GroupUrlLink urlLink = null;
       try
       {
         urlLink = new GroupUrlLink(link);
       } catch (Exception foo)
       {
-        jalview.bin.Cache.log.error("Exception for GroupURLLink '" + link
+        Cache.log.error("Exception for GroupURLLink '" + link
                 + "'", foo);
         continue;
       }
       ;
       if (!urlLink.isValid())
       {
-        jalview.bin.Cache.log.error(urlLink.getInvalidMessage());
+        Cache.log.error(urlLink.getInvalidMessage());
         continue;
       }
       final String label = urlLink.getLabel();
       boolean usingNames = false;
       // Now see which parts of the group apply for this URL
       String ltarget = urlLink.getTarget(); // jalview.util.DBRefUtils.getCanonicalName(urlLink.getTarget());
-      Object[] idset = (Object[]) commonDbrefs.get(ltarget.toUpperCase());
+      Object[] idset = commonDbrefs.get(ltarget.toUpperCase());
       String[] seqstr, ids; // input to makeUrl
       if (idset != null)
       {
@@ -1062,7 +1065,7 @@ public class PopupMenu extends JPopupMenu
             new Object[] { urlgenerator.getUrl_prefix(),
                 urlgenerator.getNumberInvolved(urlstub) }));
     // TODO: put in info about what is being sent.
-    item.addActionListener(new java.awt.event.ActionListener()
+    item.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
@@ -1076,7 +1079,7 @@ public class PopupMenu extends JPopupMenu
             try
             {
               showLink(urlgenerator.constructFrom(urlstub));
-            } catch (UrlStringTooLongException e)
+            } catch (UrlStringTooLongException e2)
             {
             }
           }
@@ -2387,11 +2390,11 @@ public class PopupMenu extends JPopupMenu
       @Override
       public void run()
       {
-        boolean isNuclueotide = ap.alignFrame.getViewport().getAlignment()
+        boolean isNucleotide = ap.alignFrame.getViewport().getAlignment()
                 .isNucleotide();
 
-        new jalview.ws.DBRefFetcher(sequences, ap.alignFrame, null,
-                ap.alignFrame.featureSettings, isNuclueotide)
+        new DBRefFetcher(sequences, ap.alignFrame, null,
+                ap.alignFrame.featureSettings, isNucleotide)
                 .fetchDBRefs(false);
 
       }
index c7e1332..afc93e0 100755 (executable)
@@ -30,6 +30,7 @@ import jalview.jbgui.GPreferences;
 import jalview.jbgui.GSequenceLink;
 import jalview.schemes.ColourSchemeProperty;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.sifts.SiftsSettings;
 
 import java.awt.BorderLayout;
@@ -40,7 +41,7 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 import java.io.File;
-import java.util.Collection;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.StringTokenizer;
 import java.util.Vector;
@@ -96,7 +97,7 @@ public class Preferences extends GPreferences
    * Holds name and link separated with | character. Sequence ID must be
    * $SEQUENCE_ID$ or $SEQUENCE_ID=/.possible | chars ./=$
    */
-  public static Vector sequenceURLLinks;
+  public static Vector<String> sequenceURLLinks;
 
   /**
    * Holds name and link separated with | character. Sequence IDS and Sequences
@@ -106,14 +107,14 @@ public class Preferences extends GPreferences
    * (TODO: proper escape for using | to separate ids or sequences
    */
 
-  public static Vector groupURLLinks;
+  public static List<String> groupURLLinks;
   static
   {
     String string = Cache
             .getDefault(
                     "SEQUENCE_LINKS",
                     "EMBL-EBI Search|http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$");
-    sequenceURLLinks = new Vector();
+    sequenceURLLinks = new Vector<String>();
 
     try
     {
@@ -152,10 +153,10 @@ public class Preferences extends GPreferences
      * .properties file as '|' separated strings
      */
 
-    groupURLLinks = new Vector();
+    groupURLLinks = new ArrayList<String>();
   }
 
-  Vector nameLinks, urlLinks;
+  Vector<String> nameLinks, urlLinks;
 
   JInternalFrame frame;
 
@@ -163,6 +164,16 @@ public class Preferences extends GPreferences
 
   private WsPreferences wsPrefs;
 
+  private OptionsParam promptEachTimeOpt = new OptionsParam(
+          MessageManager.getString("label.prompt_each_time"),
+          "Prompt each time");
+
+  private OptionsParam lineArtOpt = new OptionsParam(
+          MessageManager.getString("label.lineart"), "Lineart");
+
+  private OptionsParam textOpt = new OptionsParam(
+          MessageManager.getString("action.text"), "Text");
+
   /**
    * Creates a new Preferences object.
    */
@@ -176,7 +187,8 @@ public class Preferences extends GPreferences
     wsPrefs = new WsPreferences();
     wsTab.add(wsPrefs, BorderLayout.CENTER);
     int width = 500, height = 450;
-    if (new jalview.util.Platform().isAMac())
+    new jalview.util.Platform();
+    if (Platform.isAMac())
     {
       width = 570;
       height = 480;
@@ -329,8 +341,8 @@ public class Preferences extends GPreferences
     /*
      * Set Connections tab defaults
      */
-    nameLinks = new Vector();
-    urlLinks = new Vector();
+    nameLinks = new Vector<String>();
+    urlLinks = new Vector<String>();
     for (int i = 0; i < sequenceURLLinks.size(); i++)
     {
       String link = sequenceURLLinks.elementAt(i).toString();
@@ -357,12 +369,23 @@ public class Preferences extends GPreferences
     /*
      * Set Output tab defaults
      */
-    epsRendering
-            .addItem(MessageManager.getString("label.prompt_each_time"));
-    epsRendering.addItem(MessageManager.getString("label.lineart"));
-    epsRendering.addItem(MessageManager.getString("action.text"));
-    epsRendering.setSelectedItem(Cache.getDefault("EPS_RENDERING",
-            "Prompt each time"));
+    epsRendering.addItem(promptEachTimeOpt);
+    epsRendering.addItem(lineArtOpt);
+    epsRendering.addItem(textOpt);
+    String defaultEPS = Cache.getDefault("EPS_RENDERING",
+            "Prompt each time");
+    if (defaultEPS.equalsIgnoreCase("Text"))
+    {
+      epsRendering.setSelectedItem(textOpt);
+    }
+    else if (defaultEPS.equalsIgnoreCase("Lineart"))
+    {
+      epsRendering.setSelectedItem(lineArtOpt);
+    }
+    else
+    {
+      epsRendering.setSelectedItem(promptEachTimeOpt);
+    }
     autoIdWidth.setSelected(Cache.getDefault("FIGURE_AUTOIDWIDTH", false));
     userIdWidth.setEnabled(!autoIdWidth.isSelected());
     userIdWidthlabel.setEnabled(!autoIdWidth.isSelected());
@@ -513,15 +536,8 @@ public class Preferences extends GPreferences
     /*
      * Save Output settings
      */
-    if (epsRendering.getSelectedItem().equals("Prompt each time"))
-    {
-      Cache.applicationProperties.remove("EPS_RENDERING");
-    }
-    else
-    {
-      Cache.applicationProperties.setProperty("EPS_RENDERING", epsRendering
-              .getSelectedItem().toString());
-    }
+      Cache.applicationProperties.setProperty("EPS_RENDERING",
+              ((OptionsParam) epsRendering.getSelectedItem()).getCode());
 
     /*
      * Save Connections settings
@@ -533,7 +549,7 @@ public class Preferences extends GPreferences
     if (nameLinks.size() > 0)
     {
       StringBuffer links = new StringBuffer();
-      sequenceURLLinks = new Vector();
+      sequenceURLLinks = new Vector<String>();
       for (int i = 0; i < nameLinks.size(); i++)
       {
         sequenceURLLinks.addElement(nameLinks.elementAt(i) + "|"
@@ -849,7 +865,7 @@ public class Preferences extends GPreferences
     super.showunconserved_actionPerformed(e);
   }
 
-  public static Collection getGroupURLLinks()
+  public static List<String> getGroupURLLinks()
   {
     return groupURLLinks;
   }
@@ -981,4 +997,57 @@ public class Preferences extends GPreferences
     }
   }
 
+  public class OptionsParam
+  {
+    private String name;
+
+    private String code;
+
+    public OptionsParam(String name, String code)
+    {
+      this.name = name;
+      this.code = code;
+    }
+
+    public String getName()
+    {
+      return name;
+    }
+
+    public void setName(String name)
+    {
+      this.name = name;
+    }
+
+    public String getCode()
+    {
+      return code;
+    }
+
+    public void setCode(String code)
+    {
+      this.code = code;
+    }
+
+    @Override
+    public String toString()
+    {
+      return name;
+    }
+
+    @Override
+    public boolean equals(Object that)
+    {
+      if (!(that instanceof OptionsParam))
+      {
+        return false;
+      }
+      return this.code.equalsIgnoreCase(((OptionsParam) that).code);
+    }
+
+    @Override
+    public int hashCode(){
+      return name.hashCode() + code.hashCode();
+    }
+  }
 }
index 41de58f..2165b2c 100755 (executable)
@@ -35,25 +35,22 @@ import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
+import java.util.List;
 
 import javax.swing.JMenuItem;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
-import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
 /**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision$
+ * The panel containing the sequence ruler (when not in wrapped mode), and
+ * supports a range of mouse operations to select, hide or reveal columns.
  */
 public class ScalePanel extends JPanel implements MouseMotionListener,
         MouseListener
 {
   protected int offy = 4;
 
-  /** DOCUMENT ME!! */
   public int width;
 
   protected AlignViewport av;
@@ -62,13 +59,26 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
 
   boolean stretchingGroup = false;
 
-  int min; // used by mouseDragged to see if user
+  /*
+   * min, max hold the extent of a mouse drag action
+   */
+  int min;
 
-  int max; // used by mouseDragged to see if user
+  int max;
 
   boolean mouseDragging = false;
 
-  // wants to delete columns
+  /*
+   * holds a hidden column range when the mouse is over an adjacent column
+   */
+  int[] reveal;
+
+  /**
+   * Constructor
+   * 
+   * @param av
+   * @param ap
+   */
   public ScalePanel(AlignViewport av, AlignmentPanel ap)
   {
     this.av = av;
@@ -107,7 +117,7 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     min = res;
     max = res;
 
-    if (SwingUtilities.isRightMouseButton(evt))
+    if (evt.isPopupTrigger())
     {
       rightMouseButtonPressed(evt, res);
     }
@@ -393,6 +403,8 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
   @Override
   public void mouseMoved(MouseEvent evt)
   {
+    this.setToolTipText(null);
+    reveal = null;
     if (!av.hasHiddenColumns())
     {
       return;
@@ -402,7 +414,6 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
 
     res = av.getColumnSelection().adjustForHiddenColumns(res);
 
-    reveal = null;
     if (av.getColumnSelection().getHiddenColumns() != null)
     {
       for (int[] region : av.getColumnSelection().getHiddenColumns())
@@ -415,17 +426,11 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
                   .getString("label.reveal_hidden_columns"));
           break;
         }
-        else
-        {
-          this.setToolTipText(null);
-        }
       }
     }
     repaint();
   }
 
-  int[] reveal;
-
   /**
    * DOCUMENT ME!
    * 
@@ -460,7 +465,6 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     ColumnSelection cs = av.getColumnSelection();
     int avCharWidth = av.getCharWidth(), avCharHeight = av.getCharHeight();
 
-    int s;
     if (cs != null)
     {
       gg.setColor(new Color(220, 0, 0));
@@ -489,50 +493,15 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
         }
       }
     }
-    // Draw the scale numbers
-    gg.setColor(Color.black);
 
-    int scalestartx = (startx / 10) * 10;
     int widthx = 1 + endx - startx;
 
     FontMetrics fm = gg.getFontMetrics(av.getFont());
-    int y = avCharHeight - fm.getDescent();
-
-    if ((scalestartx % 10) == 0)
-    {
-      scalestartx += 5;
-    }
-
-    String string;
-    int maxX = 0;
-
-    for (int i = scalestartx; i < endx; i += 5)
-    {
-      if ((i % 10) == 0)
-      {
-        string = String.valueOf(av.getColumnSelection()
-                .adjustForHiddenColumns(i));
-        if ((i - startx - 1) * avCharWidth > maxX)
-        {
-          gg.drawString(string, (i - startx - 1) * avCharWidth, y);
-          maxX = (i - startx + 1) * avCharWidth + fm.stringWidth(string);
-        }
-
-        gg.drawLine(((i - startx - 1) * avCharWidth) + (avCharWidth / 2),
-                y + 2,
-                ((i - startx - 1) * avCharWidth) + (avCharWidth / 2), y
-                        + (fm.getDescent() * 2));
-      }
-      else
-      {
-        gg.drawLine(((i - startx - 1) * avCharWidth) + (avCharWidth / 2), y
-                + fm.getDescent(), ((i - startx - 1) * avCharWidth)
-                + (avCharWidth / 2), y + (fm.getDescent() * 2));
-      }
-    }
-
+    int y = avCharHeight, yOf = fm.getDescent();
+    y -= yOf;
     if (av.hasHiddenColumns())
     {
+      // draw any hidden column markers
       gg.setColor(Color.blue);
       int res;
       if (av.getShowHiddenMarkers()
@@ -549,14 +518,47 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
             continue;
           }
 
-          gg.fillPolygon(new int[] { res * avCharWidth - avCharHeight / 4,
-              res * avCharWidth + avCharHeight / 4, res * avCharWidth },
-                  new int[] { y - avCharHeight / 2, y - avCharHeight / 2,
-                      y + 8 }, 3);
+          gg.fillPolygon(new int[] {
+              -1 + res * avCharWidth - avCharHeight / 4,
+              -1 + res * avCharWidth + avCharHeight / 4,
+              -1 + res * avCharWidth }, new int[] { y, y, y + 2 * yOf }, 3);
 
         }
       }
+    }
+    // Draw the scale numbers
+    gg.setColor(Color.black);
 
+    int maxX = 0;
+    List<Object[]> marks = jalview.renderer.ScaleRenderer.calculateMarks(
+            av, startx, endx);
+
+    for (Object[] mark : marks)
+    {
+      boolean major = Boolean.valueOf((Boolean) mark[0]);
+      int mpos = ((Integer) mark[1]).intValue(); // (i - startx - 1)
+      String mstring = (String) mark[2];
+      if (mstring != null)
+      {
+        if (mpos * avCharWidth > maxX)
+        {
+          gg.drawString(mstring, mpos * avCharWidth, y);
+          maxX = (mpos + 2) * avCharWidth + fm.stringWidth(mstring);
+        }
+      }
+      if (major)
+      {
+        gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + 2,
+                (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2));
+      }
+      else
+      {
+        gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + yOf,
+                (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2));
+      }
+    }
+    if (av.hasHiddenColumns())
+    {
       if (reveal != null && reveal[0] > startx && reveal[0] < endx)
       {
         gg.drawString(MessageManager.getString("label.reveal_columns"),
@@ -565,4 +567,5 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
     }
 
   }
+
 }
index 0f24b4b..2f7cd76 100755 (executable)
@@ -122,24 +122,26 @@ public class SeqCanvas extends JComponent
   private void drawNorthScale(Graphics g, int startx, int endx, int ypos)
   {
     updateViewport();
-    int scalestartx = startx - (startx % 10) + 10;
-
-    g.setColor(Color.black);
-    // NORTH SCALE
-    for (int i = scalestartx; i < endx; i += 10)
+    for (Object[] mark : jalview.renderer.ScaleRenderer.calculateMarks(av,
+            startx, endx))
     {
-      int value = i;
-      if (av.hasHiddenColumns())
+      int mpos = ((Integer) mark[1]).intValue(); // (i - startx - 1)
+      if (mpos < 0)
       {
-        value = av.getColumnSelection().adjustForHiddenColumns(value);
+        continue;
       }
+      String mstring = (String) mark[2];
 
-      g.drawString(String.valueOf(value), (i - startx - 1) * charWidth,
-              ypos - (charHeight / 2));
-
-      g.drawLine(((i - startx - 1) * charWidth) + (charWidth / 2),
-              (ypos + 2) - (charHeight / 2), ((i - startx - 1) * charWidth)
-                      + (charWidth / 2), ypos - 2);
+      if (Boolean.valueOf((Boolean) mark[0]))
+      {
+        if (mstring != null)
+        {
+          g.drawString(mstring, mpos * charWidth, ypos - (charHeight / 2));
+        }
+        g.drawLine((mpos * charWidth) + (charWidth / 2), (ypos + 2)
+                - (charHeight / 2), (mpos * charWidth) + (charWidth / 2),
+                ypos - 2);
+      }
     }
   }
 
index 903f868..9006262 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.gui;
 
 import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.commands.EditCommand.Edit;
@@ -54,11 +55,12 @@ import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.Vector;
 
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
 /**
@@ -509,6 +511,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
   void insertNucAtCursor(boolean group, String nuc)
   {
+    // TODO not called - delete?
     groupEditing = group;
     startseq = seqCanvas.cursorY;
     lastres = seqCanvas.cursorX;
@@ -599,7 +602,7 @@ public class SeqPanel extends JPanel implements MouseListener,
   {
     lastMousePress = evt.getPoint();
 
-    if (javax.swing.SwingUtilities.isMiddleMouseButton(evt))
+    if (SwingUtilities.isMiddleMouseButton(evt))
     {
       mouseWheelPressed = true;
       return;
@@ -1185,8 +1188,7 @@ public class SeqPanel extends JPanel implements MouseListener,
           {
             for (int j = 0; j < startres - lastres; j++)
             {
-              if (!jalview.util.Comparison.isGap(groupSeqs[g]
-                      .getCharAt(fixedRight - j)))
+              if (!Comparison.isGap(groupSeqs[g].getCharAt(fixedRight - j)))
               {
                 blank = false;
                 break;
@@ -1248,7 +1250,7 @@ public class SeqPanel extends JPanel implements MouseListener,
               continue;
             }
 
-            if (!jalview.util.Comparison.isGap(groupSeqs[g].getCharAt(j)))
+            if (!Comparison.isGap(groupSeqs[g].getCharAt(j)))
             {
               // Not a gap, block edit not valid
               endEditing();
@@ -1380,7 +1382,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
       for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)
       {
-        if (jalview.util.Comparison.isGap(seq[s].getCharAt(blankColumn)))
+        if (Comparison.isGap(seq[s].getCharAt(blankColumn)))
         {
           // Theres a space, so break and insert the gap
           break;
@@ -1614,24 +1616,24 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     }
 
-    if (javax.swing.SwingUtilities.isRightMouseButton(evt))
+    if (evt.isPopupTrigger())
     {
       List<SequenceFeature> allFeatures = ap.getFeatureRenderer()
               .findFeaturesAtRes(sequence.getDatasetSequence(),
                       sequence.findPosition(res));
-      Vector links = new Vector();
+      List<String> links = new ArrayList<String>();
       for (SequenceFeature sf : allFeatures)
       {
         if (sf.links != null)
         {
-          for (int j = 0; j < sf.links.size(); j++)
+          for (String link : sf.links)
           {
-            links.addElement(sf.links.elementAt(j));
+            links.add(link);
           }
         }
       }
 
-      jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(ap, null, links);
+      PopupMenu pop = new PopupMenu(ap, null, links);
       pop.show(this, evt.getX(), evt.getY());
       return;
     }
@@ -1978,7 +1980,7 @@ public class SeqPanel extends JPanel implements MouseListener,
     {
       if (av.getAlignment() == null)
       {
-        jalview.bin.Cache.log.warn("alignviewport av SeqSetId="
+        Cache.log.warn("alignviewport av SeqSetId="
                 + av.getSequenceSetId() + " ViewId=" + av.getViewId()
                 + " 's alignment is NULL! returning immediately.");
         return;
@@ -2082,7 +2084,6 @@ public class SeqPanel extends JPanel implements MouseListener,
     ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
             av);
     av.setColumnSelection(cs);
-    av.isColSelChanged(true);
 
     PaintRefresher.Refresh(this, av.getSequenceSetId());
 
index 784474e..71c8a39 100755 (executable)
 package jalview.gui;
 
 import jalview.api.FeatureSettingsModelI;
+import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.fts.service.pdb.PDBFTSPanel;
+import jalview.fts.service.uniprot.UniprotFTSPanel;
+import jalview.io.gff.SequenceOntologyI;
 import jalview.util.DBRefUtils;
 import jalview.util.MessageManager;
 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
@@ -112,6 +116,11 @@ public class SequenceFetcher extends JPanel implements Runnable
 
   int debounceTrap = 0;
 
+  public JTextArea getTextArea()
+  {
+    return textArea;
+  }
+
   /**
    * Blocking method that initialises and returns the shared instance of the
    * SequenceFetcher client
@@ -156,9 +165,9 @@ public class SequenceFetcher extends JPanel implements Runnable
       }
     }
     if (sfetch == null
-            || dasRegistry != jalview.bin.Cache.getDasSourceRegistry()
-            || lastDasSourceRegistry != (jalview.bin.Cache
-                    .getDasSourceRegistry().getDasRegistryURL() + jalview.bin.Cache
+            || dasRegistry != Cache.getDasSourceRegistry()
+            || lastDasSourceRegistry != (Cache.getDasSourceRegistry()
+                    .getDasRegistryURL() + Cache
                     .getDasSourceRegistry().getLocalSourceString())
                     .hashCode())
     {
@@ -173,7 +182,7 @@ public class SequenceFetcher extends JPanel implements Runnable
                 .getString("status.init_sequence_database_fetchers"),
                 Thread.currentThread().hashCode());
       }
-      dasRegistry = jalview.bin.Cache.getDasSourceRegistry();
+      dasRegistry = Cache.getDasSourceRegistry();
       dasRegistry.refreshSources();
 
       jalview.ws.SequenceFetcher sf = new jalview.ws.SequenceFetcher();
@@ -365,15 +374,21 @@ public class SequenceFetcher extends JPanel implements Runnable
       {
         debounceTrap++;
         String currentSelection = database.getSelectedItem();
-        if (!currentSelection.equalsIgnoreCase("pdb"))
-        {
-          otherSourceAction();
-        }
+
         if (currentSelection.equalsIgnoreCase("pdb")
                 && (database.action == KeyEvent.VK_ENTER || ((debounceTrap % 2) == 0)))
         {
           pdbSourceAction();
         }
+        else if (currentSelection.equalsIgnoreCase("uniprot")
+                && (database.action == KeyEvent.VK_ENTER || ((debounceTrap % 2) == 0)))
+        {
+          uniprotSourceAction();
+        }
+        else
+        {
+          otherSourceAction();
+        }
         database.action = -1;
       }
     });
@@ -392,15 +407,25 @@ public class SequenceFetcher extends JPanel implements Runnable
     this.add(jPanel2, java.awt.BorderLayout.NORTH);
     jScrollPane1.getViewport().add(textArea);
 
+    /*
+     * open the database tree
+     */
+    database.waitForInput();
   }
 
   private void pdbSourceAction()
   {
     databaseButt.setText(database.getSelectedItem());
-    new PDBSearchPanel(this);
+    new PDBFTSPanel(this);
     frame.dispose();
   }
 
+  private void uniprotSourceAction()
+  {
+    databaseButt.setText(database.getSelectedItem());
+    new UniprotFTSPanel(this);
+    frame.dispose();
+  }
   private void otherSourceAction()
   {
     try
@@ -517,30 +542,31 @@ public class SequenceFetcher extends JPanel implements Runnable
     // TODO: Refactor to GUI independent code and write tests.
     // indicate if successive sources should be merged into one alignment.
     boolean addToLast = false;
-    ArrayList<String> aresultq = new ArrayList<String>(), presultTitle = new ArrayList<String>();
-    ArrayList<AlignmentI> presult = new ArrayList<AlignmentI>(), aresult = new ArrayList<AlignmentI>();
+    List<String> aresultq = new ArrayList<String>();
+    List<String> presultTitle = new ArrayList<String>();
+    List<AlignmentI> presult = new ArrayList<AlignmentI>();
+    List<AlignmentI> aresult = new ArrayList<AlignmentI>();
     Iterator<DbSourceProxy> proxies = database.getSelectedSources()
             .iterator();
     String[] qries;
-    List<String> nextfetch = Arrays.asList(qries = textArea.getText()
+    List<String> nextFetch = Arrays.asList(qries = textArea.getText()
             .split(";"));
     Iterator<String> en = Arrays.asList(new String[0]).iterator();
     int nqueries = qries.length;
 
     FeatureSettingsModelI preferredFeatureColours = null;
-    while (proxies.hasNext() && (en.hasNext() || nextfetch.size() > 0))
+    while (proxies.hasNext() && (en.hasNext() || nextFetch.size() > 0))
     {
-      if (!en.hasNext() && nextfetch.size() > 0)
+      if (!en.hasNext() && nextFetch.size() > 0)
       {
-        en = nextfetch.iterator();
-        nqueries = nextfetch.size();
+        en = nextFetch.iterator();
+        nqueries = nextFetch.size();
         // save the remaining queries in the original array
-        qries = nextfetch.toArray(new String[nqueries]);
-        nextfetch = new ArrayList<String>();
+        qries = nextFetch.toArray(new String[nqueries]);
+        nextFetch = new ArrayList<String>();
       }
 
       DbSourceProxy proxy = proxies.next();
-      boolean isAliSource = false;
       try
       {
         // update status
@@ -551,121 +577,27 @@ public class SequenceFetcher extends JPanel implements Runnable
                             Integer.valueOf(nqueries).toString(),
                             proxy.getDbName() }), Thread.currentThread()
                         .hashCode());
-        isAliSource = proxy.isAlignmentSource();
-        if (proxy.getAccessionSeparator() == null)
+        if (proxy.getMaximumQueryCount() == 1)
         {
+          /*
+           * proxy only handles one accession id at a time
+           */
           while (en.hasNext())
           {
-            String item = en.next();
-            try
+            String acc = en.next();
+            if (!fetchSingleAccession(proxy, acc, aresultq, aresult))
             {
-              if (aresult != null)
-              {
-                try
-                {
-                  // give the server a chance to breathe
-                  Thread.sleep(5);
-                } catch (Exception e)
-                {
-                  //
-                }
-
-              }
-
-              AlignmentI indres = null;
-              try
-              {
-                indres = proxy.getSequenceRecords(item);
-              } catch (OutOfMemoryError oome)
-              {
-                new OOMWarning("fetching " + item + " from "
-                        + proxy.getDbName(), oome, this);
-              }
-              if (indres != null)
-              {
-                aresultq.add(item);
-                aresult.add(indres);
-              }
-              else
-              {
-                nextfetch.add(item);
-              }
-            } catch (Exception e)
-            {
-              jalview.bin.Cache.log.info("Error retrieving " + item
-                      + " from " + proxy.getDbName(), e);
-              nextfetch.add(item);
+              nextFetch.add(acc);
             }
           }
         }
         else
         {
-          StringBuffer multiacc = new StringBuffer();
-          ArrayList<String> tosend = new ArrayList<String>();
-          while (en.hasNext())
-          {
-            String nel = en.next();
-            tosend.add(nel);
-            multiacc.append(nel);
-            if (en.hasNext())
-            {
-              multiacc.append(proxy.getAccessionSeparator());
-            }
-          }
-          try
-          {
-            AlignmentI rslt;
-            SequenceI[] rs;
-            List<String> nores = new ArrayList<String>();
-            rslt = proxy.getSequenceRecords(multiacc.toString());
-            if (rslt == null || rslt.getHeight() == 0)
-            {
-              // no results - pass on all queries to next source
-              nextfetch.addAll(tosend);
-            }
-            else
-            {
-              aresultq.add(multiacc.toString());
-              aresult.add(rslt);
-
-              rs = rslt.getSequencesArray();
-              // search for each query in the dbrefs associated with each
-              // sequence
-              // returned.
-              // ones we do not find will be used to query next source (if any)
-              for (String q : tosend)
-              {
-                DBRefEntry dbr = new DBRefEntry(), found[] = null;
-                dbr.setSource(proxy.getDbSource());
-                dbr.setVersion(null);
-                String accId = proxy.getAccessionIdFromQuery(q);
-                dbr.setAccessionId(accId);
-                boolean rfound = false;
-                for (int r = 0; r < rs.length; r++)
-                {
-                  if (rs[r] != null)
-                  {
-                    found = DBRefUtils.searchRefs(rs[r].getDBRefs(), accId);
-                    if (found != null && found.length > 0)
-                    {
-                      rfound = true;
-                      rs[r] = null;
-                    }
-                  }
-                }
-                if (!rfound)
-                {
-                  nextfetch.add(q);
-                }
-              }
-            }
-          } catch (OutOfMemoryError oome)
-          {
-            new OOMWarning("fetching " + multiacc + " from "
-                    + database.getSelectedItem(), oome, this);
-          }
+          /*
+           * proxy can fetch multiple accessions at one time
+           */
+          fetchMultipleAccessions(proxy, en, aresultq, aresult, nextFetch);
         }
-
       } catch (Exception e)
       {
         showErrorMessage("Error retrieving " + textArea.getText()
@@ -678,7 +610,6 @@ public class SequenceFetcher extends JPanel implements Runnable
         e.printStackTrace();
       } catch (OutOfMemoryError e)
       {
-        // resets dialog box - so we don't use OOMwarning here.
         showErrorMessage("Out of Memory when retrieving "
                 + textArea.getText()
                 + " from "
@@ -691,6 +622,7 @@ public class SequenceFetcher extends JPanel implements Runnable
                 + " from " + database.getSelectedItem());
         e.printStackTrace();
       }
+
       // Stack results ready for opening in alignment windows
       if (aresult != null && aresult.size() > 0)
       {
@@ -702,7 +634,7 @@ public class SequenceFetcher extends JPanel implements Runnable
         }
 
         AlignmentI ar = null;
-        if (isAliSource)
+        if (proxy.isAlignmentSource())
         {
           addToLast = false;
           // new window for each result
@@ -732,7 +664,6 @@ public class SequenceFetcher extends JPanel implements Runnable
             {
               ar.append(aresult.remove(0));
             }
-            ;
           }
           addToLast = true;
           presult.add(ar);
@@ -756,14 +687,14 @@ public class SequenceFetcher extends JPanel implements Runnable
     }
     // only remove visual delay after we finished parsing.
     guiWindow.setProgressBar(null, Thread.currentThread().hashCode());
-    if (nextfetch.size() > 0)
+    if (nextFetch.size() > 0)
     {
       StringBuffer sb = new StringBuffer();
       sb.append("Didn't retrieve the following "
-              + (nextfetch.size() == 1 ? "query" : nextfetch.size()
+              + (nextFetch.size() == 1 ? "query" : nextFetch.size()
                       + " queries") + ": \n");
       int l = sb.length(), lr = 0;
-      for (String s : nextfetch)
+      for (String s : nextFetch)
       {
         if (l != sb.length())
         {
@@ -781,6 +712,161 @@ public class SequenceFetcher extends JPanel implements Runnable
   }
 
   /**
+   * Tries to fetch one or more accession ids from the database proxy
+   * 
+   * @param proxy
+   * @param accessions
+   *          the queries to fetch
+   * @param aresultq
+   *          a successful queries list to add to
+   * @param aresult
+   *          a list of retrieved alignments to add to
+   * @param nextFetch
+   *          failed queries are added to this list
+   * @throws Exception
+   */
+  void fetchMultipleAccessions(DbSourceProxy proxy,
+          Iterator<String> accessions, List<String> aresultq,
+          List<AlignmentI> aresult, List<String> nextFetch)
+          throws Exception
+  {
+    StringBuilder multiacc = new StringBuilder();
+    List<String> tosend = new ArrayList<String>();
+    while (accessions.hasNext())
+    {
+      String nel = accessions.next();
+      tosend.add(nel);
+      multiacc.append(nel);
+      if (accessions.hasNext())
+      {
+        multiacc.append(proxy.getAccessionSeparator());
+      }
+    }
+
+    try
+    {
+      String query = multiacc.toString();
+      AlignmentI rslt = proxy.getSequenceRecords(query);
+      if (rslt == null || rslt.getHeight() == 0)
+      {
+        // no results - pass on all queries to next source
+        nextFetch.addAll(tosend);
+      }
+      else
+      {
+        aresultq.add(query);
+        aresult.add(rslt);
+        if (tosend.size() > 1)
+        {
+          checkResultForQueries(rslt, tosend, nextFetch, proxy);
+        }
+      }
+    } catch (OutOfMemoryError oome)
+    {
+      new OOMWarning("fetching " + multiacc + " from "
+              + database.getSelectedItem(), oome, this);
+    }
+  }
+
+  /**
+   * Query for a single accession id via the database proxy
+   * 
+   * @param proxy
+   * @param accession
+   * @param aresultq
+   *          a list of successful queries to add to
+   * @param aresult
+   *          a list of retrieved alignments to add to
+   * @return true if the fetch was successful, else false
+   */
+  boolean fetchSingleAccession(DbSourceProxy proxy, String accession,
+          List<String> aresultq, List<AlignmentI> aresult)
+  {
+    boolean success = false;
+    try
+    {
+      if (aresult != null)
+      {
+        try
+        {
+          // give the server a chance to breathe
+          Thread.sleep(5);
+        } catch (Exception e)
+        {
+          //
+        }
+      }
+
+      AlignmentI indres = null;
+      try
+      {
+        indres = proxy.getSequenceRecords(accession);
+      } catch (OutOfMemoryError oome)
+      {
+        new OOMWarning("fetching " + accession + " from "
+                + proxy.getDbName(), oome, this);
+      }
+      if (indres != null)
+      {
+        aresultq.add(accession);
+        aresult.add(indres);
+        success = true;
+      }
+    } catch (Exception e)
+    {
+      Cache.log.info(
+              "Error retrieving " + accession
+              + " from " + proxy.getDbName(), e);
+    } finally
+    {
+      return success;
+    }
+  }
+
+  /**
+   * Checks which of the queries were successfully retrieved by searching the
+   * DBRefs of the retrieved sequences for a match. Any not found are added to
+   * the 'nextFetch' list.
+   * 
+   * @param rslt
+   * @param queries
+   * @param nextFetch
+   * @param proxy
+   */
+  void checkResultForQueries(AlignmentI rslt, List<String> queries,
+          List<String> nextFetch, DbSourceProxy proxy)
+  {
+    SequenceI[] rs = rslt.getSequencesArray();
+
+    for (String q : queries)
+    {
+      DBRefEntry[] found = null;
+      DBRefEntry dbr = new DBRefEntry();
+      dbr.setSource(proxy.getDbSource());
+      dbr.setVersion(null);
+      String accId = proxy.getAccessionIdFromQuery(q);
+      dbr.setAccessionId(accId);
+      boolean rfound = false;
+      for (int r = 0; r < rs.length; r++)
+      {
+        if (rs[r] != null)
+        {
+          found = DBRefUtils.searchRefs(rs[r].getDBRefs(), accId);
+          if (found != null && found.length > 0)
+          {
+            rfound = true;
+            break;
+          }
+        }
+      }
+      if (!rfound)
+      {
+        nextFetch.add(q);
+      }
+    }
+  }
+
+  /**
    * 
    * @return a standard title for any results retrieved using the currently
    *         selected source and settings
@@ -833,8 +919,13 @@ public class SequenceFetcher extends JPanel implements Runnable
 
         if (preferredFeatureColours != null)
         {
-          af.viewport.applyFeaturesStyle(preferredFeatureColours);
+          af.getViewport().applyFeaturesStyle(preferredFeatureColours);
         }
+        if (Cache.getDefault("HIDE_INTRONS", true))
+        {
+          af.hideFeatureColumns(SequenceOntologyI.EXON, false);
+        }
+
         Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH,
                 AlignFrame.DEFAULT_HEIGHT);
 
@@ -843,7 +934,7 @@ public class SequenceFetcher extends JPanel implements Runnable
 
         try
         {
-          af.setMaximum(jalview.bin.Cache.getDefault("SHOW_FULLSCREEN",
+          af.setMaximum(Cache.getDefault("SHOW_FULLSCREEN",
                   false));
         } catch (Exception ex)
         {
index 5709ac9..46731e9 100644 (file)
@@ -26,16 +26,17 @@ import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.service.pdb.PDBFTSRestClient;
 import jalview.jbgui.GStructureChooser;
-import jalview.jbgui.PDBDocFieldPreferences;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
-import jalview.ws.dbsources.PDBRestClient;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
+import jalview.ws.DBRefFetcher;
 import jalview.ws.sifts.SiftsSettings;
-import jalview.ws.uimodel.PDBRestRequest;
-import jalview.ws.uimodel.PDBRestResponse;
-import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
 
 import java.awt.event.ItemEvent;
 import java.util.ArrayList;
@@ -70,11 +71,11 @@ public class StructureChooser extends GStructureChooser implements
 
   private IProgressIndicator progressIndicator;
 
-  private Collection<PDBResponseSummary> discoveredStructuresSet;
+  private Collection<FTSData> discoveredStructuresSet;
 
-  private PDBRestRequest lastPdbRequest;
+  private FTSRestRequest lastPdbRequest;
 
-  private PDBRestClient pdbRestCleint;
+  private FTSRestClientI pdbRestCleint;
 
   private String selectedPdbFileName;
 
@@ -146,22 +147,22 @@ public class StructureChooser extends GStructureChooser implements
   public void fetchStructuresMetaData()
   {
     long startTime = System.currentTimeMillis();
-    Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
-            .getStructureSummaryFields();
+    pdbRestCleint = PDBFTSRestClient.getInstance();
+    Collection<FTSDataColumnI> wantedFields = pdbRestCleint
+            .getAllDefaulDisplayedDataColumns();
 
-    discoveredStructuresSet = new LinkedHashSet<PDBResponseSummary>();
+    discoveredStructuresSet = new LinkedHashSet<FTSData>();
     HashSet<String> errors = new HashSet<String>();
     for (SequenceI seq : selectedSequences)
     {
-      PDBRestRequest pdbRequest = new PDBRestRequest();
+      FTSRestRequest pdbRequest = new FTSRestRequest();
       pdbRequest.setAllowEmptySeq(false);
       pdbRequest.setResponseSize(500);
       pdbRequest.setFieldToSearchBy("(");
       pdbRequest.setWantedFields(wantedFields);
       pdbRequest.setSearchTerm(buildQuery(seq) + ")");
       pdbRequest.setAssociatedSequence(seq);
-      pdbRestCleint = new PDBRestClient();
-      PDBRestResponse resultList;
+      FTSRestResponse resultList;
       try
       {
         resultList = pdbRestCleint.executeRequest(pdbRequest);
@@ -185,7 +186,7 @@ public class StructureChooser extends GStructureChooser implements
     if (discoveredStructuresSet != null
             && !discoveredStructuresSet.isEmpty())
     {
-      tbl_summary.setModel(PDBRestResponse.getTableModel(lastPdbRequest,
+      tbl_summary.setModel(FTSRestResponse.getTableModel(lastPdbRequest,
               discoveredStructuresSet));
       structuresDiscovered = true;
       noOfStructuresFound = discoveredStructuresSet.size();
@@ -255,7 +256,7 @@ public class StructureChooser extends GStructureChooser implements
       {
         if (isValidSeqName(entry.getId()))
         {
-          queryBuilder.append(PDBRestClient.PDBDocField.PDB_ID.getCode())
+          queryBuilder.append("pdb_id")
                   .append(":")
 .append(entry.getId().toLowerCase())
                   .append(" OR ");
@@ -274,12 +275,11 @@ public class StructureChooser extends GStructureChooser implements
           if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
           {
             queryBuilder
-                    .append(PDBRestClient.PDBDocField.UNIPROT_ACCESSION
-                            .getCode()).append(":")
+.append("uniprot_accession").append(":")
                     .append(getDBRefId(dbRef))
                     .append(" OR ");
             queryBuilder
-                    .append(PDBRestClient.PDBDocField.UNIPROT_ID.getCode())
+.append("uniprot_id")
                     .append(":")
                     .append(getDBRefId(dbRef)).append(" OR ");
             isUniProtRefsFound = true;
@@ -287,7 +287,7 @@ public class StructureChooser extends GStructureChooser implements
           else if (dbRef.getSource().equalsIgnoreCase(DBRefSource.PDB))
           {
 
-            queryBuilder.append(PDBRestClient.PDBDocField.PDB_ID.getCode())
+            queryBuilder.append("pdb_id")
                     .append(":").append(getDBRefId(dbRef).toLowerCase())
                     .append(" OR ");
             isPDBRefsFound = true;
@@ -395,15 +395,16 @@ public class StructureChooser extends GStructureChooser implements
       public void run()
       {
         long startTime = System.currentTimeMillis();
+        pdbRestCleint = PDBFTSRestClient.getInstance();
         lbl_loading.setVisible(true);
-        Collection<PDBDocField> wantedFields = PDBDocFieldPreferences
-                .getStructureSummaryFields();
-        Collection<PDBResponseSummary> filteredResponse = new HashSet<PDBResponseSummary>();
+        Collection<FTSDataColumnI> wantedFields = pdbRestCleint
+                .getAllDefaulDisplayedDataColumns();
+        Collection<FTSData> filteredResponse = new HashSet<FTSData>();
         HashSet<String> errors = new HashSet<String>();
 
         for (SequenceI seq : selectedSequences)
         {
-          PDBRestRequest pdbRequest = new PDBRestRequest();
+          FTSRestRequest pdbRequest = new FTSRestRequest();
           if (fieldToFilterBy.equalsIgnoreCase("uniprot_coverage"))
           {
             System.out.println(">>>>>> Filtering with uniprot coverate");
@@ -428,8 +429,7 @@ public class StructureChooser extends GStructureChooser implements
             pdbRequest.setWantedFields(wantedFields);
             pdbRequest.setAssociatedSequence(seq);
           }
-          pdbRestCleint = new PDBRestClient();
-          PDBRestResponse resultList;
+          FTSRestResponse resultList;
           try
           {
             resultList = pdbRestCleint.executeRequest(pdbRequest);
@@ -452,13 +452,13 @@ public class StructureChooser extends GStructureChooser implements
         if (!filteredResponse.isEmpty())
         {
           final int filterResponseCount = filteredResponse.size();
-          Collection<PDBResponseSummary> reorderedStructuresSet = new LinkedHashSet<PDBResponseSummary>();
+          Collection<FTSData> reorderedStructuresSet = new LinkedHashSet<FTSData>();
           reorderedStructuresSet.addAll(filteredResponse);
           reorderedStructuresSet.addAll(discoveredStructuresSet);
-          tbl_summary.setModel(PDBRestResponse.getTableModel(
+          tbl_summary.setModel(FTSRestResponse.getTableModel(
                   lastPdbRequest, reorderedStructuresSet));
 
-          PDBRestResponse.configureTableColumn(tbl_summary, wantedFields);
+          FTSRestResponse.configureTableColumn(tbl_summary, wantedFields);
           tbl_summary.getColumn("Ref Sequence").setPreferredWidth(120);
           tbl_summary.getColumn("Ref Sequence").setMinWidth(100);
           tbl_summary.getColumn("Ref Sequence").setMaxWidth(200);
@@ -529,17 +529,17 @@ public class StructureChooser extends GStructureChooser implements
     if (isStructuresDiscovered())
     {
       cmb_filterOption.addItem(new FilterOption("Best Quality",
-              PDBDocField.OVERALL_QUALITY.getCode(), VIEWS_FILTER));
+              "overall_quality", VIEWS_FILTER));
       cmb_filterOption.addItem(new FilterOption("Most UniProt Coverage",
-              PDBDocField.UNIPROT_COVERAGE.getCode(), VIEWS_FILTER));
+              "uniprot_coverage", VIEWS_FILTER));
       cmb_filterOption.addItem(new FilterOption("Best Resolution",
-              PDBDocField.RESOLUTION.getCode(), VIEWS_FILTER));
+              "resolution", VIEWS_FILTER));
       cmb_filterOption.addItem(new FilterOption("Most Protein Chain",
-              PDBDocField.PROTEIN_CHAIN_COUNT.getCode(), VIEWS_FILTER));
+              "number_of_protein_chains", VIEWS_FILTER));
       cmb_filterOption.addItem(new FilterOption("Most Bound Molecules",
-              PDBDocField.BOUND_MOLECULE_COUNT.getCode(), VIEWS_FILTER));
+              "number_of_bound_molecules", VIEWS_FILTER));
       cmb_filterOption.addItem(new FilterOption("Most Polymer Residues",
-              PDBDocField.POLYMER_RESIDUE_COUNT.getCode(), VIEWS_FILTER));
+              "number_of_polymer_residues", VIEWS_FILTER));
     }
     cmb_filterOption.addItem(new FilterOption("Enter PDB Id", "-",
             VIEWS_ENTER_ID));
@@ -730,8 +730,8 @@ public class StructureChooser extends GStructureChooser implements
     String currentView = selectedFilterOpt.getView();
     if (currentView == VIEWS_FILTER)
     {
-      int pdbIdColIndex = tbl_summary.getColumn(
-              PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
+          int pdbIdColIndex = tbl_summary.getColumn("PDB Id")
+                  .getModelIndex();
       int refSeqColIndex = tbl_summary.getColumn("Ref Sequence")
               .getModelIndex();
       int[] selectedRows = tbl_summary.getSelectedRows();
@@ -769,8 +769,8 @@ public class StructureChooser extends GStructureChooser implements
       int[] selectedRows = tbl_local_pdb.getSelectedRows();
       PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
       int count = 0;
-      int pdbIdColIndex = tbl_local_pdb.getColumn(
-              PDBRestClient.PDBDocField.PDB_ID.getName()).getModelIndex();
+          int pdbIdColIndex = tbl_local_pdb.getColumn("PDB Id")
+                  .getModelIndex();
       int refSeqColIndex = tbl_local_pdb.getColumn("Ref Sequence")
               .getModelIndex();
       ArrayList<SequenceI> selectedSeqsToView = new ArrayList<SequenceI>();
@@ -801,6 +801,10 @@ public class StructureChooser extends GStructureChooser implements
       if (pdbEntry == null)
       {
         pdbEntry = new PDBEntry();
+            if (pdbIdStr.split(":").length > 1)
+            {
+              pdbEntry.setChainCode(pdbIdStr.split(":")[1]);
+            }
         pdbEntry.setId(pdbIdStr);
         pdbEntry.setType(PDBEntry.Type.PDB);
         selectedSequence.getDatasetSequence().addPDBId(pdbEntry);
@@ -861,31 +865,30 @@ public class StructureChooser extends GStructureChooser implements
         {
           ssm.setProgressBar(null);
           ssm.setProgressBar("Fetching Database refs..");
-          new jalview.ws.DBRefFetcher(sequences, null, null, null, false)
-                  .fetchDBRefs(true);
+          new DBRefFetcher(sequences).fetchDBRefs(true);
           break;
         }
       }
     }
-        if (pdbEntriesToView.length > 1)
-        {
-          ArrayList<SequenceI[]> seqsMap = new ArrayList<SequenceI[]>();
-          for (SequenceI seq : sequences)
-          {
-            seqsMap.add(new SequenceI[] { seq });
-          }
-          SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]);
+    if (pdbEntriesToView.length > 1)
+    {
+      ArrayList<SequenceI[]> seqsMap = new ArrayList<SequenceI[]>();
+      for (SequenceI seq : sequences)
+      {
+        seqsMap.add(new SequenceI[] { seq });
+      }
+      SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]);
       ssm.setProgressBar(null);
       ssm.setProgressBar("Fetching PDB Structures for selected entries..");
-          sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel);
-        }
-        else
-        {
+      sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel);
+    }
+    else
+    {
       ssm.setProgressBar(null);
       ssm.setProgressBar("Fetching PDB Structure for "
               + pdbEntriesToView[0].getId());
-          sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
-        }
+      sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
+    }
   }
 
   /**
@@ -927,7 +930,7 @@ public class StructureChooser extends GStructureChooser implements
     this.structuresDiscovered = structuresDiscovered;
   }
 
-  public Collection<PDBResponseSummary> getDiscoveredStructuresSet()
+  public Collection<FTSData> getDiscoveredStructuresSet()
   {
     return discoveredStructuresSet;
   }
@@ -944,18 +947,21 @@ public class StructureChooser extends GStructureChooser implements
         isValidPBDEntry = false;
         if (txt_search.getText().length() > 0)
         {
-          List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
-          wantedFields.add(PDBDocField.PDB_ID);
-          PDBRestRequest pdbRequest = new PDBRestRequest();
+          String searchTerm = txt_search.getText().toLowerCase();
+          searchTerm = searchTerm.split(":")[0];
+          System.out.println(">>>>> search term : " + searchTerm);
+          List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+          FTSRestRequest pdbRequest = new FTSRestRequest();
           pdbRequest.setAllowEmptySeq(false);
           pdbRequest.setResponseSize(1);
           pdbRequest.setFieldToSearchBy("(pdb_id:");
           pdbRequest.setWantedFields(wantedFields);
           pdbRequest
-                  .setSearchTerm(txt_search.getText().toLowerCase() + ")");
+.setSearchTerm(searchTerm + ")");
           pdbRequest.setAssociatedSequence(selectedSequence);
-          pdbRestCleint = new PDBRestClient();
-          PDBRestResponse resultList;
+          pdbRestCleint = PDBFTSRestClient.getInstance();
+          wantedFields.add(pdbRestCleint.getPrimaryKeyColumn());
+          FTSRestResponse resultList;
           try
           {
             resultList = pdbRestCleint.executeRequest(pdbRequest);
index 90c74be..f21c5e7 100755 (executable)
@@ -59,7 +59,6 @@ import java.util.Vector;
 import javax.swing.JColorChooser;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
-import javax.swing.SwingUtilities;
 import javax.swing.ToolTipManager;
 
 /**
@@ -791,7 +790,7 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable,
   {
     if (highlightNode != null)
     {
-      if (SwingUtilities.isRightMouseButton(evt))
+      if (evt.isPopupTrigger())
       {
         Color col = JColorChooser.showDialog(this,
                 MessageManager.getString("label.select_subtree_colour"),
index 9522144..d78350d 100755 (executable)
@@ -161,6 +161,7 @@ public class TreePanel extends GTreePanel
 
     av.addPropertyChangeListener(new java.beans.PropertyChangeListener()
     {
+      @Override
       public void propertyChange(PropertyChangeEvent evt)
       {
         if (evt.getPropertyName().equals("alignment"))
@@ -196,6 +197,7 @@ public class TreePanel extends GTreePanel
 
   }
 
+  @Override
   public void viewMenu_menuSelected()
   {
     buildAssociatedViewMenu();
@@ -231,6 +233,7 @@ public class TreePanel extends GTreePanel
       buttonGroup.add(item);
       item.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent evt)
         {
           treeCanvas.applyToAllViews = false;
@@ -249,6 +252,7 @@ public class TreePanel extends GTreePanel
     itemf.setSelected(treeCanvas.applyToAllViews);
     itemf.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent evt)
       {
         treeCanvas.applyToAllViews = itemf.isSelected();
@@ -276,6 +280,7 @@ public class TreePanel extends GTreePanel
       }
     }
 
+    @Override
     public void run()
     {
 
@@ -389,6 +394,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void textbox_actionPerformed(ActionEvent e)
   {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
@@ -434,6 +440,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void saveAsNewick_actionPerformed(ActionEvent e)
   {
     JalviewFileChooser chooser = new JalviewFileChooser(
@@ -474,12 +481,14 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void printMenu_actionPerformed(ActionEvent e)
   {
     // Putting in a thread avoids Swing painting problems
     treeCanvas.startPrinting();
   }
 
+  @Override
   public void originalSeqData_actionPerformed(ActionEvent e)
   {
     if (!tree.hasOriginalSequenceData())
@@ -547,6 +556,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void fitToWindow_actionPerformed(ActionEvent e)
   {
     treeCanvas.fitToWindow = fitToWindow.isSelected();
@@ -637,6 +647,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void font_actionPerformed(ActionEvent e)
   {
     if (treeCanvas == null)
@@ -666,6 +677,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void distanceMenu_actionPerformed(ActionEvent e)
   {
     treeCanvas.setShowDistances(distanceMenu.isSelected());
@@ -677,6 +689,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void bootstrapMenu_actionPerformed(ActionEvent e)
   {
     treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
@@ -688,6 +701,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void placeholdersMenu_actionPerformed(ActionEvent e)
   {
     treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
@@ -699,6 +713,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void epsTree_actionPerformed(ActionEvent e)
   {
     boolean accurateText = true;
@@ -772,6 +787,7 @@ public class TreePanel extends GTreePanel
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void pngTree_actionPerformed(ActionEvent e)
   {
     int width = treeCanvas.getWidth();
@@ -828,6 +844,7 @@ public class TreePanel extends GTreePanel
     tree.applyToNodes(new NodeTransformI()
     {
 
+      @Override
       public void transform(BinaryNode node)
       {
         if (node instanceof SequenceNode
index 65fd72f..1a639f1 100755 (executable)
@@ -330,7 +330,7 @@ public class AppletFormatAdapter
       }
       else if (format.equals(IdentifyFile.FeaturesFile))
       {
-        alignFile = new FeaturesFile(inFile, type);
+        alignFile = new FeaturesFile(true, inFile, type);
       }
       return buildAlignmentFrom(alignFile);
     } catch (Exception e)
index 2dd5f26..372d905 100755 (executable)
@@ -842,12 +842,12 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
         features = sequences[i].getSequenceFeatures();
         if (features != null)
         {
-          for (int j = 0; j < features.length; j++)
+          for (SequenceFeature sequenceFeature : features)
           {
-            isnonpos = features[j].begin == 0 && features[j].end == 0;
+            isnonpos = sequenceFeature.begin == 0 && sequenceFeature.end == 0;
             if ((!nonpos && isnonpos)
                     || (!isnonpos && visOnly && !visible
-                            .containsKey(features[j].type)))
+                            .containsKey(sequenceFeature.type)))
             {
               // skip if feature is nonpos and we ignore them or if we only
               // output visible and it isn't non-pos and it's not visible
@@ -855,47 +855,48 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
             }
 
             if (group != null
-                    && (features[j].featureGroup == null || !features[j].featureGroup
+                    && (sequenceFeature.featureGroup == null || !sequenceFeature.featureGroup
                             .equals(group)))
             {
               continue;
             }
 
-            if (group == null && features[j].featureGroup != null)
+            if (group == null && sequenceFeature.featureGroup != null)
             {
               continue;
             }
             // we have features to output
             featuresGen = true;
-            if (features[j].description == null
-                    || features[j].description.equals(""))
+            if (sequenceFeature.description == null
+                    || sequenceFeature.description.equals(""))
             {
-              out.append(features[j].type).append(TAB);
+              out.append(sequenceFeature.type).append(TAB);
             }
             else
             {
-              if (features[j].links != null
-                      && features[j].getDescription().indexOf("<html>") == -1)
+              if (sequenceFeature.links != null
+                      && sequenceFeature.getDescription().indexOf("<html>") == -1)
               {
                 out.append("<html>");
               }
 
-              out.append(features[j].description + " ");
-              if (features[j].links != null)
+              out.append(sequenceFeature.description);
+              if (sequenceFeature.links != null)
               {
-                for (int l = 0; l < features[j].links.size(); l++)
+                for (int l = 0; l < sequenceFeature.links.size(); l++)
                 {
-                  String label = features[j].links.elementAt(l).toString();
+                  String label = sequenceFeature.links.elementAt(l);
                   String href = label.substring(label.indexOf("|") + 1);
                   label = label.substring(0, label.indexOf("|"));
 
-                  if (features[j].description.indexOf(href) == -1)
+                  if (sequenceFeature.description.indexOf(href) == -1)
                   {
-                    out.append("<a href=\"" + href + "\">" + label + "</a>");
+                    out.append(" <a href=\"" + href + "\">" + label
+                            + "</a>");
                   }
                 }
 
-                if (features[j].getDescription().indexOf("</html>") == -1)
+                if (sequenceFeature.getDescription().indexOf("</html>") == -1)
                 {
                   out.append("</html>");
                 }
@@ -905,15 +906,15 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
             }
             out.append(sequences[i].getName());
             out.append("\t-1\t");
-            out.append(features[j].begin);
+            out.append(sequenceFeature.begin);
             out.append(TAB);
-            out.append(features[j].end);
+            out.append(sequenceFeature.end);
             out.append(TAB);
-            out.append(features[j].type);
-            if (!Float.isNaN(features[j].score))
+            out.append(sequenceFeature.type);
+            if (!Float.isNaN(sequenceFeature.score))
             {
               out.append(TAB);
-              out.append(features[j].score);
+              out.append(sequenceFeature.score);
             }
             out.append(newline);
           }
@@ -1024,7 +1025,8 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
           boolean includeNonPositionalFeatures)
   {
     StringBuilder out = new StringBuilder(256);
-    out.append(String.format("%s %d\n", GFF_VERSION, gffVersion));
+    int version = gffVersion == 0 ? 2 : gffVersion;
+    out.append(String.format("%s %d\n", GFF_VERSION, version));
     String source;
     boolean isnonpos;
     for (SequenceI seq : sequences)
index e554b8e..00af242 100644 (file)
@@ -124,7 +124,7 @@ public class HtmlSvgOutput
         }
       }
 
-      if (renderStyle.equalsIgnoreCase("lineart"))
+      if (renderStyle.equalsIgnoreCase("Lineart"))
       {
         g1.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
                 SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
index 71f4237..889359f 100755 (executable)
@@ -230,7 +230,6 @@ public class IdentifyFile
                 } catch (IOException ex)
                 {
                 }
-                ;
                 if (dta != null && dta.indexOf("*") > -1)
                 {
                   starterm = true;
@@ -250,34 +249,19 @@ public class IdentifyFile
           // read as a FASTA (probably)
           break;
         }
-        if ((data.indexOf("<") > -1)) // possible Markup Language data i.e HTML,
+        int lessThan = data.indexOf("<");
+        if ((lessThan > -1)) // possible Markup Language data i.e HTML,
                                       // RNAML, XML
         {
-          // FIXME this is nuts - it consumes the rest of the file if no match
-          boolean identified = false;
-          do
-          {
-            if (data.matches("<(?i)html(\"[^\"]*\"|'[^']*'|[^'\">])*>"))
-            {
-              reply = HtmlFile.FILE_DESC;
-              identified = true;
-              break;
-            }
-
-            if (data.matches("<(?i)rnaml (\"[^\"]*\"|'[^']*'|[^'\">])*>"))
-            {
-              reply = "RNAML";
-              identified = true;
-              break;
-            }
-          } while ((data = source.nextLine()) != null);
-
-          if (identified)
+          String upper = data.toUpperCase();
+          if (upper.substring(lessThan).startsWith("<HTML"))
           {
+            reply = HtmlFile.FILE_DESC;
             break;
           }
-          if (data == null)
+          if (upper.substring(lessThan).startsWith("<RNAML"))
           {
+            reply = "RNAML";
             break;
           }
         }
index d3a1d09..2d76d6b 100644 (file)
@@ -23,11 +23,13 @@ package jalview.io;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
+import jalview.io.gff.GffConstants;
+import jalview.util.DBRefUtils;
 import jalview.util.UrlLink;
 
 import java.util.ArrayList;
-import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 
 /**
  * generate HTML reports for a sequence
@@ -44,165 +46,189 @@ public class SequenceAnnotationReport
   }
 
   /**
-   * appends the features at rpos to the given stringbuffer ready for display in
-   * a tooltip
+   * Append text for the list of features to the tooltip
    * 
    * @param tooltipText2
-   * @param linkImageURL
    * @param rpos
    * @param features
-   *          TODO refactor to Jalview 'utilities' somehow.
+   * @param minmax
    */
   public void appendFeatures(final StringBuffer tooltipText2, int rpos,
-          List<SequenceFeature> features)
+          List<SequenceFeature> features, Map<String, float[][]> minmax)
   {
-    appendFeatures(tooltipText2, rpos, features, null);
+    if (features != null)
+    {
+      for (SequenceFeature feature : features)
+      {
+        appendFeature(tooltipText2, rpos, minmax, feature);
+      }
+    }
   }
 
-  public void appendFeatures(final StringBuffer tooltipText2, int rpos,
-          List<SequenceFeature> features, Hashtable minmax)
+  /**
+   * Appends text for one sequence feature to the string buffer
+   * 
+   * @param sb
+   * @param rpos
+   * @param minmax
+   *          {{min, max}, {min, max}} positional and non-positional feature
+   *          scores for this type
+   * @param feature
+   */
+  void appendFeature(final StringBuffer sb, int rpos,
+          Map<String, float[][]> minmax, SequenceFeature feature)
   {
-    String tmpString;
-    if (features != null)
+    if ("disulfide bond".equals(feature.getType()))
     {
-      for (SequenceFeature feature : features)
+      if (feature.getBegin() == rpos || feature.getEnd() == rpos)
       {
-        if (feature.getType().equals("disulfide bond"))
+        if (sb.length() > 6)
         {
-          if (feature.getBegin() == rpos || feature.getEnd() == rpos)
-          {
-            if (tooltipText2.length() > 6)
-            {
-              tooltipText2.append("<br>");
-            }
-            tooltipText2.append("disulfide bond " + feature.getBegin()
-                    + ":" + feature.getEnd());
-          }
+          sb.append("<br>");
         }
-        else
+        sb.append("disulfide bond ").append(feature.getBegin()).append(":")
+                .append(feature.getEnd());
+      }
+    }
+    else
+    {
+      if (sb.length() > 6)
+      {
+        sb.append("<br>");
+      }
+      // TODO: remove this hack to display link only features
+      boolean linkOnly = feature.getValue("linkonly") != null;
+      if (!linkOnly)
+      {
+        sb.append(feature.getType()).append(" ");
+        if (rpos != 0)
         {
-          if (tooltipText2.length() > 6)
+          // we are marking a positional feature
+          sb.append(feature.begin);
+        }
+        if (feature.begin != feature.end)
+        {
+          sb.append(" " + feature.end);
+        }
+
+        if (feature.getDescription() != null
+                && !feature.description.equals(feature.getType()))
+        {
+          String tmpString = feature.getDescription();
+          String tmp2up = tmpString.toUpperCase();
+          final int startTag = tmp2up.indexOf("<HTML>");
+          if (startTag > -1)
           {
-            tooltipText2.append("<br>");
+            tmpString = tmpString.substring(startTag + 6);
+            tmp2up = tmp2up.substring(startTag + 6);
           }
-          // TODO: remove this hack to display link only features
-          boolean linkOnly = feature.getValue("linkonly") != null;
-          if (!linkOnly)
+          // TODO strips off </body> but not <body> - is that intended?
+          int endTag = tmp2up.indexOf("</BODY>");
+          if (endTag > -1)
           {
-            tooltipText2.append(feature.getType() + " ");
-            if (rpos != 0)
-            {
-              // we are marking a positional feature
-              tooltipText2.append(feature.begin);
-            }
-            if (feature.begin != feature.end)
-            {
-              tooltipText2.append(" " + feature.end);
-            }
-
-            if (feature.getDescription() != null
-                    && !feature.description.equals(feature.getType()))
-            {
-              tmpString = feature.getDescription();
-              String tmp2up = tmpString.toUpperCase();
-              int startTag = tmp2up.indexOf("<HTML>");
-              if (startTag > -1)
-              {
-                tmpString = tmpString.substring(startTag + 6);
-                tmp2up = tmp2up.substring(startTag + 6);
-              }
-              int endTag = tmp2up.indexOf("</BODY>");
-              if (endTag > -1)
-              {
-                tmpString = tmpString.substring(0, endTag);
-                tmp2up = tmp2up.substring(0, endTag);
-              }
-              endTag = tmp2up.indexOf("</HTML>");
-              if (endTag > -1)
-              {
-                tmpString = tmpString.substring(0, endTag);
-              }
-
-              if (startTag > -1)
-              {
-                tooltipText2.append("; " + tmpString);
-              }
-              else
-              {
-                if (tmpString.indexOf("<") > -1
-                        || tmpString.indexOf(">") > -1)
-                {
-                  // The description does not specify html is to
-                  // be used, so we must remove < > symbols
-                  tmpString = tmpString.replaceAll("<", "&lt;");
-                  tmpString = tmpString.replaceAll(">", "&gt;");
-
-                  tooltipText2.append("; ");
-                  tooltipText2.append(tmpString);
+            tmpString = tmpString.substring(0, endTag);
+            tmp2up = tmp2up.substring(0, endTag);
+          }
+          endTag = tmp2up.indexOf("</HTML>");
+          if (endTag > -1)
+          {
+            tmpString = tmpString.substring(0, endTag);
+          }
 
-                }
-                else
-                {
-                  tooltipText2.append("; " + tmpString);
-                }
-              }
-            }
-            // check score should be shown
-            if (!Float.isNaN(feature.getScore()))
+          if (startTag > -1)
+          {
+            sb.append("; ").append(tmpString);
+          }
+          else
+          {
+            if (tmpString.indexOf("<") > -1
+                    || tmpString.indexOf(">") > -1)
             {
-              float[][] rng = (minmax == null) ? null : ((float[][]) minmax
-                      .get(feature.getType()));
-              if (rng != null && rng[0] != null && rng[0][0] != rng[0][1])
-              {
-                tooltipText2.append(" Score=" + feature.getScore());
-              }
+              // The description does not specify html is to
+              // be used, so we must remove < > symbols
+              tmpString = tmpString.replaceAll("<", "&lt;");
+              tmpString = tmpString.replaceAll(">", "&gt;");
+              sb.append("; ").append(tmpString);
             }
-            if (feature.getValue("status") != null)
+            else
             {
-              String status = feature.getValue("status").toString();
-              if (status.length() > 0)
-              {
-                tooltipText2.append("; (" + feature.getValue("status")
-                        + ")");
-              }
+              sb.append("; ").append(tmpString);
             }
           }
         }
-        if (feature.links != null)
+
+        /*
+         * score should be shown if there is one, and min != max
+         * for this feature type (e.g. not all 0)
+         */
+        if (!Float.isNaN(feature.getScore()))
         {
-          if (linkImageURL != null)
+          float[][] rng = (minmax == null) ? null : minmax.get(feature
+                  .getType());
+          if (rng != null && rng[0] != null && rng[0][0] != rng[0][1])
           {
-            tooltipText2.append(" <img src=\"" + linkImageURL + "\">");
+            sb.append(" Score=").append(
+                    String.valueOf(feature.getScore()));
           }
-          else
+        }
+        String status = (String) feature.getValue("status");
+        if (status != null && status.length() > 0)
+        {
+          sb.append("; (").append(status).append(")");
+        }
+        String clinSig = (String) feature
+                .getValue(GffConstants.CLINICAL_SIGNIFICANCE);
+        if (clinSig != null)
+        {
+          sb.append("; ").append(clinSig);
+        }
+      }
+    }
+    appendLinks(sb, feature);
+  }
+
+  /**
+   * Format and appends any hyperlinks for the sequence feature to the string
+   * buffer
+   * 
+   * @param sb
+   * @param feature
+   */
+  void appendLinks(final StringBuffer sb, SequenceFeature feature)
+  {
+    if (feature.links != null)
+    {
+      if (linkImageURL != null)
+      {
+        sb.append(" <img src=\"" + linkImageURL + "\">");
+      }
+      else
+      {
+        for (String urlstring : feature.links)
+        {
+          try
           {
-            for (String urlstring : feature.links)
+            for (String[] urllink : createLinksFrom(null, urlstring))
             {
-              try
-              {
-                for (String[] urllink : createLinksFrom(null, urlstring))
-                {
-                  tooltipText2.append("<br/> <a href=\""
-                          + urllink[3]
-                          + "\" target=\""
-                          + urllink[0]
-                          + "\">"
-                          + (urllink[0].toLowerCase().equals(
-                                  urllink[1].toLowerCase()) ? urllink[0]
-                                  : (urllink[0] + ":" + urllink[1]))
-                          + "</a></br>");
-                }
-              } catch (Exception x)
-              {
-                System.err.println("problem when creating links from "
-                        + urlstring);
-                x.printStackTrace();
-              }
+              sb.append("<br/> <a href=\""
+                      + urllink[3]
+                      + "\" target=\""
+                      + urllink[0]
+                      + "\">"
+                      + (urllink[0].toLowerCase().equals(
+                              urllink[1].toLowerCase()) ? urllink[0]
+                              : (urllink[0] + ":" + urllink[1]))
+                      + "</a></br>");
             }
+          } catch (Exception x)
+          {
+            System.err.println("problem when creating links from "
+                    + urlstring);
+            x.printStackTrace();
           }
-
         }
       }
+
     }
   }
 
@@ -213,63 +239,72 @@ public class SequenceAnnotationReport
    * @return String[][] { String[] { link target, link label, dynamic component
    *         inserted (if any), url }}
    */
-  public String[][] createLinksFrom(SequenceI seq, String link)
+  String[][] createLinksFrom(SequenceI seq, String link)
   {
-    ArrayList<String[]> urlSets = new ArrayList<String[]>();
-    ArrayList<String> uniques = new ArrayList<String>();
+    List<String[]> urlSets = new ArrayList<String[]>();
+    List<String> uniques = new ArrayList<String>();
     UrlLink urlLink = new UrlLink(link);
     if (!urlLink.isValid())
     {
       System.err.println(urlLink.getInvalidMessage());
       return null;
     }
-    final String target = urlLink.getTarget(); // link.substring(0,
-    // link.indexOf("|"));
-    final String label = urlLink.getLabel();
     if (seq != null && urlLink.isDynamic())
     {
-
-      // collect matching db-refs
-      DBRefEntry[] dbr = jalview.util.DBRefUtils.selectRefs(seq.getDBRefs(),
-              new String[] { target });
-      // collect id string too
-      String id = seq.getName();
-      String descr = seq.getDescription();
-      if (descr != null && descr.length() < 1)
+      urlSets.addAll(createDynamicLinks(seq, urlLink, uniques));
+    }
+    else
+    {
+      String target = urlLink.getTarget();
+      String label = urlLink.getLabel();
+      String unq = label + "|" + urlLink.getUrl_prefix();
+      if (!uniques.contains(unq))
       {
-        descr = null;
+        uniques.add(unq);
+        urlSets.add(new String[] { target, label, null,
+            urlLink.getUrl_prefix() });
       }
-      if (dbr != null)
+    }
+
+    return urlSets.toArray(new String[][] {});
+  }
+
+  /**
+   * Formats and returns a list of dynamic href links
+   * 
+   * @param seq
+   * @param urlLink
+   * @param uniques
+   */
+  List<String[]> createDynamicLinks(SequenceI seq, UrlLink urlLink,
+          List<String> uniques)
+  {
+    List<String[]> result = new ArrayList<String[]>();
+    final String target = urlLink.getTarget();
+    final String label = urlLink.getLabel();
+
+    // collect matching db-refs
+    DBRefEntry[] dbr = DBRefUtils.selectRefs(seq.getDBRefs(),
+            new String[] { target });
+    // collect id string too
+    String id = seq.getName();
+    String descr = seq.getDescription();
+    if (descr != null && descr.length() < 1)
+    {
+      descr = null;
+    }
+    if (dbr != null)
+    {
+      for (int r = 0; r < dbr.length; r++)
       {
-        for (int r = 0; r < dbr.length; r++)
+        if (id != null && dbr[r].getAccessionId().equals(id))
         {
-          if (id != null && dbr[r].getAccessionId().equals(id))
-          {
-            // suppress duplicate link creation for the bare sequence ID
-            // string with this link
-            id = null;
-          }
-          // create Bare ID link for this RUL
-          String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(), true);
-          if (urls != null)
-          {
-            for (int u = 0; u < urls.length; u += 2)
-            {
-              String unq = urls[u] + "|" + urls[u + 1];
-              if (!uniques.contains(unq))
-              {
-                urlSets.add(new String[] { target, label, urls[u],
-                    urls[u + 1] });
-                uniques.add(unq);
-              }
-            }
-          }
+          // suppress duplicate link creation for the bare sequence ID
+          // string with this link
+          id = null;
         }
-      }
-      if (id != null)
-      {
-        // create Bare ID link for this RUL
-        String[] urls = urlLink.makeUrls(id, true);
+        // create Bare ID link for this URL
+        String[] urls = urlLink.makeUrls(dbr[r].getAccessionId(), true);
         if (urls != null)
         {
           for (int u = 0; u < urls.length; u += 2)
@@ -277,51 +312,56 @@ public class SequenceAnnotationReport
             String unq = urls[u] + "|" + urls[u + 1];
             if (!uniques.contains(unq))
             {
-              urlSets.add(new String[] { target, label, urls[u],
+              result.add(new String[] { target, label, urls[u],
                   urls[u + 1] });
               uniques.add(unq);
             }
           }
         }
       }
-      if (descr != null && urlLink.getRegexReplace() != null)
+    }
+    if (id != null)
+    {
+      // create Bare ID link for this URL
+      String[] urls = urlLink.makeUrls(id, true);
+      if (urls != null)
       {
-        // create link for this URL from description only if regex matches
-        String[] urls = urlLink.makeUrls(descr, true);
-        if (urls != null)
+        for (int u = 0; u < urls.length; u += 2)
         {
-          for (int u = 0; u < urls.length; u += 2)
+          String unq = urls[u] + "|" + urls[u + 1];
+          if (!uniques.contains(unq))
           {
-            String unq = urls[u] + "|" + urls[u + 1];
-            if (!uniques.contains(unq))
-            {
-              urlSets.add(new String[] { target, label, urls[u],
-                  urls[u + 1] });
-              uniques.add(unq);
-            }
+            result.add(new String[] { target, label, urls[u],
+                urls[u + 1] });
+            uniques.add(unq);
           }
         }
       }
-
     }
-    else
+    if (descr != null && urlLink.getRegexReplace() != null)
     {
-      String unq = label + "|" + urlLink.getUrl_prefix();
-      if (!uniques.contains(unq))
+      // create link for this URL from description only if regex matches
+      String[] urls = urlLink.makeUrls(descr, true);
+      if (urls != null)
       {
-        uniques.add(unq);
-        // Add a non-dynamic link
-        urlSets.add(new String[] { target, label, null,
-            urlLink.getUrl_prefix() });
+        for (int u = 0; u < urls.length; u += 2)
+        {
+          String unq = urls[u] + "|" + urls[u + 1];
+          if (!uniques.contains(unq))
+          {
+            result.add(new String[] { target, label, urls[u],
+                urls[u + 1] });
+            uniques.add(unq);
+          }
+        }
       }
     }
-
-    return urlSets.toArray(new String[][] {});
+    return result;
   }
 
   public void createSequenceAnnotationReport(final StringBuffer tip,
           SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
-          Hashtable minmax)
+          Map<String, float[][]> minmax)
   {
     createSequenceAnnotationReport(tip, sequence, showDbRefs, showNpFeats,
             true, minmax);
@@ -329,7 +369,7 @@ public class SequenceAnnotationReport
 
   public void createSequenceAnnotationReport(final StringBuffer tip,
           SequenceI sequence, boolean showDbRefs, boolean showNpFeats,
-          boolean tableWrap, Hashtable minmax)
+          boolean tableWrap, Map<String, float[][]> minmax)
   {
     String tmp;
     tip.append("<i>");
index 3a7419c..af5a1fa 100644 (file)
@@ -26,6 +26,7 @@ public abstract class StructureFile extends AlignFile
   private String id;
 
   private String dbRefType;
+
   /**
    * set to true to add derived sequence annotations (temp factor read from
    * file, or computed secondary structure) to the alignment
@@ -117,14 +118,11 @@ public abstract class StructureFile extends AlignFile
     sourceDBRef.setSource(DBRefSource.PDB);
     sourceDBRef.setStartRes(pdbSequence.getStart());
     sourceDBRef.setEndRes(pdbSequence.getEnd());
-
-    SequenceI chainseq = pdbSequence.deriveSequence();
-    chainseq.setSourceDBRef(sourceDBRef);
-    chainseq.addPDBId(entry);
-    chainseq.addDBRef(sourceDBRef);
-
+    pdbSequence.setSourceDBRef(sourceDBRef);
+    pdbSequence.addPDBId(entry);
+    pdbSequence.addDBRef(sourceDBRef);
+    SequenceI chainseq = pdbSequence;
     seqs.addElement(chainseq);
-
     AlignmentAnnotation[] chainannot = chainseq.getAnnotation();
 
     if (chainannot != null && visibleChainAnnotation)
@@ -185,8 +183,7 @@ public abstract class StructureFile extends AlignFile
 
   @SuppressWarnings("unchecked")
   protected void replaceAndUpdateChains(List<SequenceI> prot,
-          AlignmentI al,
-          String pep, boolean b)
+          AlignmentI al, String pep, boolean b)
   {
     List<List<? extends Object>> replaced = AlignSeq
             .replaceMatchingSeqsWith(seqs, annotations, prot, al, pep,
@@ -259,8 +256,7 @@ public abstract class StructureFile extends AlignFile
   }
 
   @SuppressWarnings({ "unchecked", "rawtypes" })
-  private void processWithJmolParser(List<SequenceI> prot)
-          throws Exception
+  private void processWithJmolParser(List<SequenceI> prot) throws Exception
   {
     try
     {
@@ -268,13 +264,11 @@ public abstract class StructureFile extends AlignFile
       Class cl = Class.forName("jalview.ext.jmol.JmolParser");
       if (cl != null)
       {
-        final Constructor constructor = cl
-.getConstructor(new Class[] {
+        final Constructor constructor = cl.getConstructor(new Class[] {
             boolean.class, boolean.class, boolean.class, FileParse.class });
         final Object[] args = new Object[] { visibleChainAnnotation,
             predictSecondaryStructure, externalSecondaryStructure,
-            new FileParse(getDataName(),
-                type) };
+            new FileParse(getDataName(), type) };
 
         StructureViewSettings.setShowSeqFeatures(false);
         StructureViewSettings.setVisibleChainAnnotation(false);
diff --git a/src/jalview/io/gff/GffConstants.java b/src/jalview/io/gff/GffConstants.java
new file mode 100644 (file)
index 0000000..545c6e3
--- /dev/null
@@ -0,0 +1,23 @@
+package jalview.io.gff;
+
+/**
+ * A class to hold constants shared by creators and consumers of GFF or feature
+ * data
+ */
+public class GffConstants
+{
+  // SequenceOntology terms are to be found in SequenceOntologyI
+
+  /*
+   * clinical_significance may be an attribute of 
+   * sequence_variant data from Ensembl
+   */
+  public static final String CLINICAL_SIGNIFICANCE = "clinical_significance";
+
+  /*
+   * not instantiable
+   */
+  private GffConstants()
+  {
+  }
+}
index 205b9c6..b2eb094 100755 (executable)
@@ -55,7 +55,6 @@ import javax.swing.JPanel;
 import javax.swing.JRadioButtonMenuItem;
 import javax.swing.JTabbedPane;
 import javax.swing.KeyStroke;
-import javax.swing.SwingUtilities;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
@@ -179,6 +178,8 @@ public class GAlignFrame extends JInternalFrame
 
   protected JMenu showProducts = new JMenu();
 
+  protected JMenuItem runGroovy = new JMenuItem();
+
   protected JMenuItem rnahelicesColour = new JMenuItem();
 
   protected JCheckBoxMenuItem autoCalculate = new JCheckBoxMenuItem();
@@ -293,8 +294,7 @@ public class GAlignFrame extends JInternalFrame
           @Override
           public void mousePressed(MouseEvent evt)
           {
-            if (evt.isControlDown()
-                    || SwingUtilities.isRightMouseButton(evt))
+            if (evt.isPopupTrigger())
             {
               radioItem.removeActionListener(radioItem.getActionListeners()[0]);
 
@@ -1726,6 +1726,17 @@ public class GAlignFrame extends JInternalFrame
     // for show products actions see AlignFrame.canShowProducts
     showProducts.setText(MessageManager.getString("label.get_cross_refs"));
 
+    runGroovy.setText(MessageManager.getString("label.run_groovy"));
+    runGroovy.setToolTipText(MessageManager.getString("label.run_groovy_tip"));
+    runGroovy.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        runGroovy_actionPerformed();
+      }
+    });
+
     JMenuItem openFeatureSettings = new JMenuItem(
             MessageManager.getString("action.feature_settings"));
     openFeatureSettings.addActionListener(new ActionListener()
@@ -2160,6 +2171,7 @@ public class GAlignFrame extends JInternalFrame
     alignFrameMenuBar.add(colourMenu);
     alignFrameMenuBar.add(calculateMenu);
     alignFrameMenuBar.add(webService);
+
     fileMenu.add(fetchSequence);
     fileMenu.add(addSequenceMenu);
     fileMenu.add(reload);
@@ -2178,6 +2190,9 @@ public class GAlignFrame extends JInternalFrame
     fileMenu.add(associatedData);
     fileMenu.addSeparator();
     fileMenu.add(closeMenuItem);
+
+    pasteMenu.add(pasteNew);
+    pasteMenu.add(pasteThis);
     editMenu.add(undoMenuItem);
     editMenu.add(redoMenuItem);
     editMenu.add(cut);
@@ -2198,6 +2213,13 @@ public class GAlignFrame extends JInternalFrame
     // editMenu.addSeparator();
     editMenu.add(padGapsMenuitem);
 
+    showMenu.add(showAllColumns);
+    showMenu.add(showAllSeqs);
+    showMenu.add(showAllhidden);
+    hideMenu.add(hideSelColumns);
+    hideMenu.add(hideSelSequences);
+    hideMenu.add(hideAllSelection);
+    hideMenu.add(hideAllButSelection);
     viewMenu.add(newView);
     viewMenu.add(expandViews);
     viewMenu.add(gatherViews);
@@ -2268,6 +2290,12 @@ public class GAlignFrame extends JInternalFrame
     colourMenu.add(modifyPID);
     colourMenu.add(annotationColour);
     colourMenu.add(rnahelicesColour);
+
+    sort.add(sortIDMenuItem);
+    sort.add(sortLengthMenuItem);
+    sort.add(sortGroupMenuItem);
+    sort.add(sortPairwiseMenuItem);
+    sort.add(sortByTreeMenu);
     calculateMenu.add(sort);
     calculateMenu.add(calculateTree);
     calculateMenu.addSeparator();
@@ -2281,17 +2309,14 @@ public class GAlignFrame extends JInternalFrame
     calculateMenu.add(autoCalculate);
     calculateMenu.add(sortByTree);
     calculateMenu.addSeparator();
+    calculateMenu.add(expandAlignment);
     calculateMenu.add(extractScores);
+    calculateMenu.addSeparator();
+    calculateMenu.add(runGroovy);
+
     webServiceNoServices = new JMenuItem(
             MessageManager.getString("label.no_services"));
     webService.add(webServiceNoServices);
-    pasteMenu.add(pasteNew);
-    pasteMenu.add(pasteThis);
-    sort.add(sortIDMenuItem);
-    sort.add(sortLengthMenuItem);
-    sort.add(sortGroupMenuItem);
-    sort.add(sortPairwiseMenuItem);
-    sort.add(sortByTreeMenu);
     exportImageMenu.add(htmlMenuItem);
     exportImageMenu.add(epsFile);
     exportImageMenu.add(createPNG);
@@ -2303,13 +2328,6 @@ public class GAlignFrame extends JInternalFrame
     this.getContentPane().add(statusPanel, java.awt.BorderLayout.SOUTH);
     statusPanel.add(statusBar, null);
     this.getContentPane().add(tabbedPane, java.awt.BorderLayout.CENTER);
-    showMenu.add(showAllColumns);
-    showMenu.add(showAllSeqs);
-    showMenu.add(showAllhidden);
-    hideMenu.add(hideSelColumns);
-    hideMenu.add(hideSelSequences);
-    hideMenu.add(hideAllSelection);
-    hideMenu.add(hideAllButSelection);
 
     formatMenu.add(font);
     formatMenu.addSeparator();
@@ -2337,7 +2355,6 @@ public class GAlignFrame extends JInternalFrame
     selectMenu.add(grpsFromSelection);
     selectMenu.add(deleteGroups);
     selectMenu.add(annotationColumn);
-    calculateMenu.add(expandAlignment);
     // TODO - determine if the listenToViewSelections button is needed : see bug
     // JAL-574
     // selectMenu.addSeparator();
@@ -2355,6 +2372,14 @@ public class GAlignFrame extends JInternalFrame
   }
 
   /**
+   * Try to run script in a Groovy console, having first ensured that this
+   * alignframe is set as currentAlignFrame in Desktop
+   */
+  protected void runGroovy_actionPerformed()
+  {
+
+  }
+  /**
    * Adds the given action listener and key accelerator to the given menu item.
    * Also saves in a lookup table to support lookup of action by key stroke.
    * 
diff --git a/src/jalview/jbgui/GPDBSearchPanel.java b/src/jalview/jbgui/GPDBSearchPanel.java
deleted file mode 100644 (file)
index d640df8..0000000
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 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.
- */
-
-package jalview.jbgui;
-
-import jalview.gui.Desktop;
-import jalview.gui.JvSwingUtils;
-import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
-import jalview.util.MessageManager;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-
-import java.awt.BorderLayout;
-import java.awt.CardLayout;
-import java.awt.Dimension;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.Arrays;
-
-import javax.swing.ImageIcon;
-import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JFrame;
-import javax.swing.JInternalFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTabbedPane;
-import javax.swing.JTable;
-import javax.swing.JTextField;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-
-/**
- * GUI layout for PDB Fetch Panel
- * 
- * @author tcnofoegbu
- *
- */
-@SuppressWarnings("serial")
-public abstract class GPDBSearchPanel extends JPanel
-{
-  protected String frameTitle = MessageManager
-          .getString("label.pdb_sequence_getcher");
-
-  protected JInternalFrame mainFrame = new JInternalFrame(frameTitle);
-
-  protected JComboBox<PDBDocField> cmb_searchTarget = new JComboBox<PDBDocField>();
-
-  protected JButton btn_ok = new JButton();
-
-  protected JButton btn_back = new JButton();
-
-  protected JButton btn_cancel = new JButton();
-
-  protected JTextField txt_search = new JTextField(20);
-
-  protected JTable tbl_summary = new JTable()
-  {
-    @Override
-    public String getToolTipText(MouseEvent evt)
-    {
-      String toolTipText = null;
-      java.awt.Point pnt = evt.getPoint();
-      int rowIndex = rowAtPoint(pnt);
-      int colIndex = columnAtPoint(pnt);
-
-      try
-      {
-        if (getValueAt(rowIndex, colIndex) == null)
-        {
-          return null;
-        }
-        toolTipText = getValueAt(rowIndex, colIndex).toString();
-
-      } catch (Exception e)
-      {
-        e.printStackTrace();
-      }
-      toolTipText = (toolTipText == null ? null
-              : (toolTipText.length() > 500 ? JvSwingUtils.wrapTooltip(
-                      true, toolTipText.subSequence(0, 500) + "...")
-                      : JvSwingUtils.wrapTooltip(true, toolTipText)));
-
-      return toolTipText;
-    }
-  };
-
-  protected StringBuilder errorWarning = new StringBuilder();
-
-  protected JScrollPane scrl_searchResult = new JScrollPane(tbl_summary);
-
-  protected ImageIcon warningImage = new ImageIcon(getClass().getResource(
-          "/images/warning.gif"));
-
-  protected ImageIcon loadingImage = new ImageIcon(getClass().getResource(
-          "/images/loading.gif"));
-
-  protected JLabel lbl_warning = new JLabel(warningImage);
-
-  protected JLabel lbl_loading = new JLabel(loadingImage);
-
-  private JTabbedPane tabbedPane = new JTabbedPane();
-
-  private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
-          PreferenceSource.SEARCH_SUMMARY);
-
-  private JPanel pnl_actions = new JPanel();
-
-  private JPanel pnl_results = new JPanel(new CardLayout());
-
-  private JPanel pnl_inputs = new JPanel();
-
-  private BorderLayout mainLayout = new BorderLayout();
-
-  protected PDBDocField[] previousWantedFields;
-
-  public GPDBSearchPanel()
-  {
-    try
-    {
-      jbInit();
-      mainFrame.invalidate();
-      mainFrame.pack();
-    } catch (Exception e)
-    {
-      e.printStackTrace();
-    }
-  }
-
-  /**
-   * Initializes the GUI default properties
-   * 
-   * @throws Exception
-   */
-  private void jbInit() throws Exception
-  {
-    lbl_warning.setVisible(false);
-    lbl_warning.setFont(new java.awt.Font("Verdana", 0, 12));
-    lbl_loading.setVisible(false);
-    lbl_loading.setFont(new java.awt.Font("Verdana", 0, 12));
-
-    tbl_summary.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
-    tbl_summary.setAutoCreateRowSorter(true);
-    tbl_summary.getTableHeader().setReorderingAllowed(false);
-    tbl_summary.addMouseListener(new MouseAdapter()
-    {
-      @Override
-      public void mouseClicked(MouseEvent e)
-      {
-        validateSelection();
-      }
-
-      @Override
-      public void mouseReleased(MouseEvent e)
-      {
-        validateSelection();
-      }
-    });
-    tbl_summary.addKeyListener(new KeyAdapter()
-    {
-      @Override
-      public void keyPressed(KeyEvent evt)
-      {
-        validateSelection();
-        switch (evt.getKeyCode())
-        {
-        case KeyEvent.VK_ESCAPE: // escape key
-          btn_back_ActionPerformed();
-          break;
-        case KeyEvent.VK_ENTER: // enter key
-          if (btn_ok.isEnabled())
-          {
-            btn_ok_ActionPerformed();
-          }
-          evt.consume();
-          break;
-        case KeyEvent.VK_TAB: // tab key
-          if (evt.isShiftDown())
-          {
-            tabbedPane.requestFocus();
-          }
-          else
-          {
-            btn_back.requestFocus();
-          }
-          evt.consume();
-          break;
-        default:
-          return;
-        }
-      }
-    });
-
-    btn_back.setFont(new java.awt.Font("Verdana", 0, 12));
-    btn_back.setText(MessageManager.getString("action.back"));
-    btn_back.addActionListener(new java.awt.event.ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        btn_back_ActionPerformed();
-      }
-    });
-    btn_back.addKeyListener(new KeyAdapter()
-    {
-      @Override
-      public void keyPressed(KeyEvent evt)
-      {
-        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
-        {
-          btn_back_ActionPerformed();
-        }
-      }
-    });
-
-    btn_ok.setEnabled(false);
-    btn_ok.setFont(new java.awt.Font("Verdana", 0, 12));
-    btn_ok.setText(MessageManager.getString("action.ok"));
-    btn_ok.addActionListener(new java.awt.event.ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        btn_ok_ActionPerformed();
-      }
-    });
-    btn_ok.addKeyListener(new KeyAdapter()
-    {
-      @Override
-      public void keyPressed(KeyEvent evt)
-      {
-        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
-        {
-          btn_ok_ActionPerformed();
-        }
-      }
-    });
-
-    btn_cancel.setFont(new java.awt.Font("Verdana", 0, 12));
-    btn_cancel.setText(MessageManager.getString("action.cancel"));
-    btn_cancel.addActionListener(new java.awt.event.ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        btn_cancel_ActionPerformed();
-      }
-    });
-    btn_cancel.addKeyListener(new KeyAdapter()
-    {
-      @Override
-      public void keyPressed(KeyEvent evt)
-      {
-        if (evt.getKeyCode() == KeyEvent.VK_ENTER)
-        {
-          btn_cancel_ActionPerformed();
-        }
-      }
-    });
-
-    scrl_searchResult.setPreferredSize(new Dimension(500, 300));
-    scrl_searchResult
-            .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
-
-    cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12));
-    cmb_searchTarget.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        String tooltipText;
-        if ("all".equalsIgnoreCase(getCmbSearchTarget().getSelectedItem()
-                .toString()))
-        {
-          tooltipText = MessageManager.getString("label.search_all");
-        }
-        else if ("pdb id".equalsIgnoreCase(getCmbSearchTarget()
-                .getSelectedItem().toString()))
-        {
-          tooltipText = MessageManager
-                  .getString("label.separate_multiple_accession_ids");
-        }
-        else
-        {
-          tooltipText = MessageManager.formatMessage(
-                  "label.separate_multiple_query_values",
-                  new Object[] { getCmbSearchTarget().getSelectedItem()
-                          .toString() });
-        }
-        txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true,
-                tooltipText));
-        txt_search_ActionPerformed();
-      }
-    });
-
-    populateCmbSearchTargetOptions();
-
-    txt_search.setFont(new java.awt.Font("Verdana", 0, 12));
-
-    txt_search.addKeyListener(new KeyAdapter()
-    {
-      @Override
-      public void keyPressed(KeyEvent e)
-      {
-        if (e.getKeyCode() == KeyEvent.VK_ENTER)
-        {
-          if (txt_search.getText() == null
-                  || txt_search.getText().isEmpty())
-          {
-            return;
-          }
-          if ("pdb id".equalsIgnoreCase(getCmbSearchTarget()
-                  .getSelectedItem().toString()))
-          {
-            transferToSequenceFetcher(txt_search.getText());
-          }
-        }
-      }
-    });
-
-    txt_search.getDocument().addDocumentListener(new DocumentListener()
-    {
-      @Override
-      public void insertUpdate(DocumentEvent e)
-      {
-        txt_search_ActionPerformed();
-      }
-
-      @Override
-      public void removeUpdate(DocumentEvent e)
-      {
-        txt_search_ActionPerformed();
-      }
-
-      @Override
-      public void changedUpdate(DocumentEvent e)
-      {
-        txt_search_ActionPerformed();
-      }
-    });
-
-    final String searchTabTitle = MessageManager
-            .getString("label.search_result");
-    final String configureCols = MessageManager
-            .getString("label.configure_displayed_columns");
-    ChangeListener changeListener = new ChangeListener()
-    {
-      @Override
-      public void stateChanged(ChangeEvent changeEvent)
-      {
-        JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent
-                .getSource();
-        int index = sourceTabbedPane.getSelectedIndex();
-
-        btn_back.setVisible(true);
-        btn_cancel.setVisible(true);
-        btn_ok.setVisible(true);
-        if (sourceTabbedPane.getTitleAt(index).equals(configureCols))
-        {
-          btn_back.setVisible(false);
-          btn_cancel.setVisible(false);
-          btn_ok.setVisible(false);
-          btn_back.setEnabled(false);
-          btn_cancel.setEnabled(false);
-          btn_ok.setEnabled(false);
-          previousWantedFields = PDBDocFieldPreferences
-                  .getSearchSummaryFields().toArray(new PDBDocField[0]);
-        }
-        if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle))
-        {
-          btn_back.setEnabled(true);
-          btn_cancel.setEnabled(true);
-          if (wantedFieldsUpdated())
-          {
-            txt_search_ActionPerformed();
-          }
-          else
-          {
-            validateSelection();
-          }
-        }
-      }
-    };
-    tabbedPane.addChangeListener(changeListener);
-    tabbedPane.setPreferredSize(new Dimension(500, 300));
-    tabbedPane.add(searchTabTitle, scrl_searchResult);
-    tabbedPane.add(configureCols, pdbDocFieldPrefs);
-
-    pnl_actions.add(btn_back);
-    pnl_actions.add(btn_ok);
-    pnl_actions.add(btn_cancel);
-
-    pnl_results.add(tabbedPane);
-    pnl_inputs.add(cmb_searchTarget);
-    pnl_inputs.add(txt_search);
-    pnl_inputs.add(lbl_loading);
-    pnl_inputs.add(lbl_warning);
-
-    this.setLayout(mainLayout);
-    this.add(pnl_inputs, java.awt.BorderLayout.NORTH);
-    this.add(pnl_results, java.awt.BorderLayout.CENTER);
-    this.add(pnl_actions, java.awt.BorderLayout.SOUTH);
-    mainFrame.setVisible(true);
-    mainFrame.setContentPane(this);
-    mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
-    Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400);
-  }
-
-  public boolean wantedFieldsUpdated()
-  {
-    if (previousWantedFields == null)
-    {
-      return true;
-    }
-
-    return Arrays.equals(PDBDocFieldPreferences.getSearchSummaryFields()
-            .toArray(new PDBDocField[0]), previousWantedFields) ? false
-            : true;
-
-  }
-
-  public void validateSelection()
-  {
-    if (tbl_summary.getSelectedRows().length > 0)
-    {
-      btn_ok.setEnabled(true);
-    }
-    else
-    {
-      btn_ok.setEnabled(false);
-    }
-  }
-
-  public JComboBox<PDBDocField> getCmbSearchTarget()
-  {
-    return cmb_searchTarget;
-  }
-
-  public JTextField getTxtSearch()
-  {
-    return txt_search;
-  }
-
-  public JInternalFrame getMainFrame()
-  {
-    return mainFrame;
-  }
-
-  public abstract void transferToSequenceFetcher(String ids);
-
-  public abstract void txt_search_ActionPerformed();
-
-  public abstract void btn_ok_ActionPerformed();
-
-  public abstract void btn_back_ActionPerformed();
-
-  public abstract void btn_cancel_ActionPerformed();
-
-  public abstract void populateCmbSearchTargetOptions();
-
-}
index b5c1804..25727d0 100755 (executable)
  */
 package jalview.jbgui;
 
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.service.pdb.PDBFTSRestClient;
 import jalview.gui.JvSwingUtils;
 import jalview.gui.StructureViewer.ViewerType;
-import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
 import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
@@ -199,7 +201,7 @@ public class GPreferences extends JPanel
   /*
    * Output tab components
    */
-  protected JComboBox<String> epsRendering = new JComboBox<String>();
+  protected JComboBox<Object> epsRendering = new JComboBox<Object>();
 
   protected JLabel userIdWidthlabel = new JLabel();
 
@@ -899,8 +901,8 @@ public class GPreferences extends JPanel
 
     ypos += lineSpacing;
     ypos += lineSpacing;
-    PDBDocFieldPreferences docFieldPref = new PDBDocFieldPreferences(
-            PreferenceSource.PREFERENCES);
+    FTSDataColumnPreferences docFieldPref = new FTSDataColumnPreferences(
+            PreferenceSource.PREFERENCES, PDBFTSRestClient.getInstance());
     docFieldPref.setBounds(new Rectangle(10, ypos, 450, 120));
     structureTab.add(docFieldPref);
 
index 8430557..a1aa0d3 100644 (file)
 package jalview.jbgui;
 
 import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.service.pdb.PDBFTSRestClient;
 import jalview.gui.AlignmentPanel;
 import jalview.gui.Desktop;
 import jalview.gui.JvSwingUtils;
-import jalview.jbgui.PDBDocFieldPreferences.PreferenceSource;
 import jalview.util.MessageManager;
-import jalview.ws.dbsources.PDBRestClient;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
 
 import java.awt.BorderLayout;
 import java.awt.CardLayout;
@@ -60,6 +61,7 @@ import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
+import javax.swing.table.TableColumn;
 
 @SuppressWarnings("serial")
 /**
@@ -157,8 +159,65 @@ public abstract class GStructureChooser extends JPanel implements
 
   protected static final String VIEWS_LOCAL_PDB = "VIEWS_LOCAL_PDB";
 
+  protected JTable tbl_local_pdb = new JTable();
+
+  protected JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
+
+  private JTabbedPane pnl_filter = new JTabbedPane();
+
+  private FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences(
+          PreferenceSource.STRUCTURE_CHOOSER,
+          PDBFTSRestClient.getInstance());
+
+  protected FTSDataColumnI[] previousWantedFields;
+
   protected JTable tbl_summary = new JTable()
   {
+    private boolean inLayout;
+
+    @Override
+    public boolean getScrollableTracksViewportWidth()
+    {
+      return hasExcessWidth();
+
+    }
+
+    @Override
+    public void doLayout()
+    {
+      if (hasExcessWidth())
+      {
+        autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
+      }
+      inLayout = true;
+      super.doLayout();
+      inLayout = false;
+      autoResizeMode = AUTO_RESIZE_OFF;
+    }
+
+    protected boolean hasExcessWidth()
+    {
+      return getPreferredSize().width < getParent().getWidth();
+    }
+
+    @Override
+    public void columnMarginChanged(ChangeEvent e)
+    {
+      if (isEditing())
+      {
+        removeEditor();
+      }
+      TableColumn resizingColumn = getTableHeader().getResizingColumn();
+      // Need to do this here, before the parent's
+      // layout manager calls getPreferredSize().
+      if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
+              && !inLayout)
+      {
+        resizingColumn.setPreferredWidth(resizingColumn.getWidth());
+      }
+      resizeAndRepaint();
+    }
+
     @Override
     public String getToolTipText(MouseEvent evt)
     {
@@ -189,17 +248,6 @@ public abstract class GStructureChooser extends JPanel implements
 
   protected JScrollPane scrl_foundStructures = new JScrollPane(tbl_summary);
 
-  protected JTable tbl_local_pdb = new JTable();
-
-  protected JScrollPane scrl_localPDB = new JScrollPane(tbl_local_pdb);
-
-  private JTabbedPane pnl_filter = new JTabbedPane();
-
-  private PDBDocFieldPreferences pdbDocFieldPrefs = new PDBDocFieldPreferences(
-          PreferenceSource.STRUCTURE_CHOOSER);
-
-  protected PDBDocField[] previousWantedFields;
-
   public GStructureChooser()
   {
     try
@@ -473,9 +521,9 @@ public abstract class GStructureChooser extends JPanel implements
           btn_cancel.setEnabled(false);
           btn_view.setVisible(false);
           btn_cancel.setVisible(false);
-          previousWantedFields = PDBDocFieldPreferences
-                  .getStructureSummaryFields().toArray(
-                          new PDBRestClient.PDBDocField[0]);
+          previousWantedFields = PDBFTSRestClient.getInstance()
+                  .getAllDefaulDisplayedDataColumns()
+                  .toArray(new FTSDataColumnI[0]);
         }
         if (sourceTabbedPane.getTitleAt(index)
                 .equals(foundStructureSummary))
@@ -527,8 +575,10 @@ public abstract class GStructureChooser extends JPanel implements
       return true;
     }
 
-    return Arrays.equals(PDBDocFieldPreferences.getStructureSummaryFields()
-            .toArray(new PDBRestClient.PDBDocField[0]),
+    return Arrays.equals(
+            PDBFTSRestClient.getInstance()
+                    .getAllDefaulDisplayedDataColumns()
+            .toArray(new FTSDataColumnI[0]),
             previousWantedFields) ? false : true;
 
   }
diff --git a/src/jalview/renderer/ScaleRenderer.java b/src/jalview/renderer/ScaleRenderer.java
new file mode 100644 (file)
index 0000000..7f1e074
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+package jalview.renderer;
+
+import jalview.api.AlignViewportI;
+import jalview.datamodel.SequenceI;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Calculate and display alignment rulers
+ * 
+ * @author jprocter
+ *
+ */
+public class ScaleRenderer
+{
+  /**
+   * calculate positions markers on the alignment ruler
+   * 
+   * @param av
+   * @param startx
+   *          left-most column in visible view
+   * @param endx
+   *          - right-most column in visible view
+   * @return List { Object { .. } } Boolean: true/false for major/minor mark,
+   *         Integer: marker position in alignment column coords, String: null
+   *         or a String to be rendered at the position.
+   */
+  public static List<Object[]> calculateMarks(AlignViewportI av,
+          int startx, int endx)
+  {
+    new ArrayList<Object[]>();
+
+    int scalestartx = (startx / 10) * 10;
+
+    SequenceI refSeq = av.getAlignment().getSeqrep();
+    int refSp = 0, refStartI = 0, refEndI = -1;
+    if (refSeq != null)
+    {
+      // find bounds and set origin appopriately
+      // locate first visible position for this sequence
+      int[] refbounds = av.getColumnSelection()
+              .locateVisibleBoundsOfSequence(refSeq);
+
+      refSp = refbounds[0];
+      refStartI = refbounds[4];
+      refEndI = refbounds[5];
+      scalestartx = refSp + ((scalestartx - refSp) / 10) * 10;
+    }
+
+    if (refSeq == null && scalestartx % 10 == 0)
+    {
+      scalestartx += 5;
+    }
+    List<Object[]> marks = new ArrayList<Object[]>();
+    String string;
+    int refN, iadj;
+    // todo: add a 'reference origin column' to set column number relative to
+    for (int i = scalestartx; i < endx; i += 5)
+    {
+      Object[] amark = new Object[3];
+      if (((i - refSp) % 10) == 0)
+      {
+        if (refSeq == null)
+        {
+          iadj = av.getColumnSelection().adjustForHiddenColumns(i - 1) + 1;
+          string = String.valueOf(iadj);
+        }
+        else
+        {
+          iadj = av.getColumnSelection().adjustForHiddenColumns(i - 1);
+          refN = refSeq.findPosition(iadj);
+          // TODO show bounds if position is a gap
+          // - ie L--R -> "1L|2R" for
+          // marker
+          if (iadj < refStartI)
+          {
+            string = String.valueOf(iadj - refStartI);
+          }
+          else if (iadj > refEndI)
+          {
+            string = "+" + String.valueOf(iadj - refEndI);
+          }
+          else
+          {
+            string = String.valueOf(refN) + refSeq.getCharAt(iadj);
+          }
+        }
+        amark[0] = Boolean.TRUE;
+        amark[1] = Integer.valueOf(i - startx - 1);
+        amark[2] = string;
+
+      }
+      else
+      {
+        amark[0] = Boolean.FALSE;
+        amark[1] = Integer.valueOf(i - startx - 1);
+        amark[2] = null;
+      }
+      marks.add(amark);
+    }
+    return marks;
+  }
+
+}
index 2276913..0d14cd9 100644 (file)
@@ -390,7 +390,7 @@ public class FeatureRenderer extends FeatureRendererModel
 
     }
 
-    if (transparency != 1.0f && g != null && transparencyAvailable)
+    if (transparency != 1.0f && g != null)
     {
       Graphics2D g2 = (Graphics2D) g;
       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
@@ -398,19 +398,6 @@ public class FeatureRenderer extends FeatureRendererModel
     }
   }
 
-  boolean transparencyAvailable = true;
-
-  protected void setTransparencyAvailable(boolean isTransparencyAvailable)
-  {
-    transparencyAvailable = isTransparencyAvailable;
-  }
-
-  @Override
-  public boolean isTransparencyAvailable()
-  {
-    return transparencyAvailable;
-  }
-
   /**
    * Called when alignment in associated view has new/modified features to
    * discover and display.
index b8cd563..31d1ded 100644 (file)
@@ -103,4 +103,43 @@ public class ColorUtils
     return col == null ? null : col.brighter().brighter().brighter();
   }
 
+  /**
+   * Returns a color between minColour and maxColour; the RGB values are in
+   * proportion to where 'value' lies between minValue and maxValue
+   * 
+   * @param value
+   * @param minValue
+   * @param minColour
+   * @param maxValue
+   * @param maxColour
+   * @return
+   */
+  public static Color getGraduatedColour(float value, float minValue,
+          Color minColour, float maxValue, Color maxColour)
+  {
+    if (minValue == maxValue)
+    {
+      return minColour;
+    }
+    if (value < minValue)
+    {
+      value = minValue;
+    }
+    if (value > maxValue)
+    {
+      value = maxValue;
+    }
+
+    /*
+     * prop = proportion of the way value is from minValue to maxValue
+     */
+    float prop = (value - minValue) / (maxValue - minValue);
+    float r = minColour.getRed() + prop
+            * (maxColour.getRed() - minColour.getRed());
+    float g = minColour.getGreen() + prop
+            * (maxColour.getGreen() - minColour.getGreen());
+    float b = minColour.getBlue() + prop
+            * (maxColour.getBlue() - minColour.getBlue());
+    return new Color(r / 255, g / 255, b / 255);
+  }
 }
index 8902e2c..5605a53 100644 (file)
@@ -286,7 +286,7 @@ public class Comparison
    * @param letters
    * @return
    */
-  public static final boolean areNucleotide(char[][] letters)
+  static final boolean areNucleotide(char[][] letters)
   {
     int ntCount = 0;
     int aaCount = 0;
@@ -300,16 +300,11 @@ public class Comparison
       // to save a lengthy calculation
       for (char c : seq)
       {
-        if ('a' <= c && c <= 'z')
-        {
-          c -= TO_UPPER_CASE;
-        }
-
-        if (c == 'A' || c == 'G' || c == 'C' || c == 'T' || c == 'U')
+        if (isNucleotide(c))
         {
           ntCount++;
         }
-        else if (!Comparison.isGap(c))
+        else if (!isGap(c))
         {
           aaCount++;
         }
@@ -332,6 +327,59 @@ public class Comparison
   }
 
   /**
+   * Answers true if the character is one of aAcCgGtTuU
+   * 
+   * @param c
+   * @return
+   */
+  public static boolean isNucleotide(char c)
+  {
+    if ('a' <= c && c <= 'z')
+    {
+      c -= TO_UPPER_CASE;
+    }
+
+    switch (c)
+    {
+    case 'A':
+    case 'C':
+    case 'G':
+    case 'T':
+    case 'U':
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Answers true if every character in the string is one of aAcCgGtTuU, or
+   * (optionally) a gap character (dot, dash, space), else false
+   * 
+   * @param s
+   * @param allowGaps
+   * @return
+   */
+  public static boolean isNucleotideSequence(String s, boolean allowGaps)
+  {
+    if (s == null)
+    {
+      return false;
+    }
+    for (int i = 0; i < s.length(); i++)
+    {
+      char c = s.charAt(i);
+      if (!isNucleotide(c))
+      {
+        if (!allowGaps || !isGap(c))
+        {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  /**
    * Convenience overload of isNucleotide
    * 
    * @param seqs
diff --git a/src/jalview/util/DnaUtils.java b/src/jalview/util/DnaUtils.java
new file mode 100644 (file)
index 0000000..f6514e5
--- /dev/null
@@ -0,0 +1,160 @@
+package jalview.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class DnaUtils
+{
+
+  /**
+   * Parses an ENA/GenBank format location specifier and returns a list of
+   * [start, end] ranges. Returns null if not able to parse.
+   * 
+   * @param location
+   * @return
+   * @see http://www.insdc.org/files/feature_table.html#3.4
+   */
+  public static List<int[]> parseLocation(String location)
+  {
+    if (location.startsWith("join("))
+    {
+      return parseJoin(location);
+    }
+    else if (location.startsWith("complement("))
+    {
+      return parseComplement(location);
+    }
+    String errorMessage = "Unable to process location specifier: "
+            + location;
+    if (location.startsWith("order("))
+    {
+      System.err.println(errorMessage);
+      return null;
+    }
+
+    /*
+     * try to parse m..n (or simply m)
+     * also handles <m..n or m..>n (discarding < or >)
+     */
+    String[] range = location.split("\\.\\.");
+    if (range.length == 1 || range.length == 2)
+    {
+      try
+      {
+        int start = parseRangeEnd(range[0]);
+        int end = range.length == 1 ? start : parseRangeEnd(range[1]);
+        return Collections.singletonList(new int[] { start, end });
+      } catch (NumberFormatException e)
+      {
+        /*
+         * could be a location like <1..888 or 1..>888
+         */
+        System.err.println(errorMessage);
+        return null;
+      }
+    }
+    else
+    {
+      /*
+       * could be a location like 102.110 or 123^124
+       */
+      System.err.println(errorMessage);
+      return null;
+    }
+  }
+
+  /**
+   * Returns the integer value of a locus, discarding any < or > prefix
+   * 
+   * @throws NumberFormatException
+   *           if value is not numeric
+   */
+  static int parseRangeEnd(String loc)
+  {
+
+    if (loc.startsWith("<") || loc.startsWith(">"))
+    {
+      loc = loc.substring(1);
+    }
+    return Integer.valueOf(loc);
+  }
+
+  /**
+   * Parses a complement(locationSpec) into a list of start-end ranges
+   * 
+   * @param location
+   * @return
+   */
+  static List<int[]> parseComplement(String location)
+  {
+    /*
+     * take what is inside complement()
+     */
+    if (!location.endsWith(")"))
+    {
+      return null;
+    }
+    String toComplement = location.substring("complement(".length(),
+            location.length() - 1);
+    List<int[]> ranges = parseLocation(toComplement);
+    if (ranges == null)
+    {
+      /*
+       * something bad in there
+       */
+      return null;
+    }
+
+    /*
+     * reverse the order and direction of ranges
+     */
+    Collections.reverse(ranges);
+    for (int[] range : ranges)
+    {
+      int temp = range[0];
+      range[0] = range[1];
+      range[1] = temp;
+    }
+    return ranges;
+  }
+
+  /**
+   * Parses a join(loc1,loc2,...,locn) into a list of start-end ranges
+   * 
+   * @param location
+   * @return
+   */
+  static List<int[]> parseJoin(String location)
+  {
+    List<int[]> ranges = new ArrayList<int[]>();
+
+    /*
+     * take what is inside join()
+     */
+    if (!location.endsWith(")"))
+    {
+      return null;
+    }
+    String joinedLocs = location.substring("join(".length(),
+            location.length() - 1);
+    String[] locations = joinedLocs.split(",");
+    for (String loc : locations)
+    {
+      List<int[]> range = parseLocation(loc);
+      if (range == null)
+      {
+        /*
+         * something bad in there
+         */
+        return null;
+      }
+      else
+      {
+        ranges.addAll(range);
+      }
+    }
+    return ranges;
+  }
+
+}
index b7aa4ca..72fa605 100755 (executable)
@@ -272,7 +272,7 @@ MessageManager.formatMessage(
       }
     }
 
-    if (renderStyle.equalsIgnoreCase("lineart"))
+    if (renderStyle.equalsIgnoreCase("Lineart"))
     {
       ig2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE,
               SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR);
index 6044655..ccc2012 100644 (file)
@@ -299,4 +299,108 @@ public class StringUtils
     }
     return result;
   }
+
+  /**
+   * Compares two versions formatted as e.g. "3.4.5" and returns -1, 0 or 1 as
+   * the first version precedes, is equal to, or follows the second
+   * 
+   * @param v1
+   * @param v2
+   * @return
+   */
+  public static int compareVersions(String v1, String v2)
+  {
+    return compareVersions(v1, v2, null);
+  }
+
+  /**
+   * Compares two versions formatted as e.g. "3.4.5b1" and returns -1, 0 or 1 as
+   * the first version precedes, is equal to, or follows the second
+   * 
+   * @param v1
+   * @param v2
+   * @param pointSeparator
+   *          a string used to delimit point increments in sub-tokens of the
+   *          version
+   * @return
+   */
+  public static int compareVersions(String v1, String v2,
+          String pointSeparator)
+  {
+    if (v1 == null || v2 == null)
+    {
+      return 0;
+    }
+    String[] toks1 = v1.split("\\.");
+    String[] toks2 = v2.split("\\.");
+    int i = 0;
+    for (; i < toks1.length; i++)
+    {
+      if (i >= toks2.length)
+      {
+        /*
+         * extra tokens in v1
+         */
+        return 1;
+      }
+      String tok1 = toks1[i];
+      String tok2 = toks2[i];
+      if (pointSeparator != null)
+      {
+        /*
+         * convert e.g. 5b2 into decimal 5.2 for comparison purposes
+         */
+        tok1 = tok1.replace(pointSeparator, ".");
+        tok2 = tok2.replace(pointSeparator, ".");
+      }
+      try
+      {
+        float f1 = Float.valueOf(tok1);
+        float f2 = Float.valueOf(tok2);
+        int comp = Float.compare(f1, f2);
+        if (comp != 0)
+        {
+          return comp;
+        }
+      } catch (NumberFormatException e)
+      {
+        System.err.println("Invalid version format found: "
+                + e.getMessage());
+        return 0;
+      }
+    }
+
+    if (i < toks2.length)
+    {
+      /*
+       * extra tokens in v2 
+       */
+      return -1;
+    }
+
+    /*
+     * same length, all tokens match
+     */
+    return 0;
+  }
+
+  /**
+   * Converts the string to all lower-case except the first character which is
+   * upper-cased
+   * 
+   * @param s
+   * @return
+   */
+  public static String toSentenceCase(String s)
+  {
+    if (s == null)
+    {
+      return s;
+    }
+    if (s.length() <= 1)
+    {
+      return s.toUpperCase();
+    }
+    return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
+  }
 }
index b70e92b..c16fdce 100644 (file)
@@ -1078,6 +1078,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       updateHiddenColumns();
     }
+    isColSelChanged(true);
   }
 
   /**
@@ -1206,8 +1207,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
    */
   public boolean isColSelChanged(boolean b)
   {
-    int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel
-            .hashCode();
+    int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
     if (hc != -1 && hc != colselhash)
     {
       if (b)
@@ -1308,7 +1308,7 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
     colSel.hideSelectedColumns();
     setSelectionGroup(null);
-
+    isColSelChanged(true);
   }
 
   public void hideColumns(int start, int end)
@@ -1321,17 +1321,19 @@ public abstract class AlignmentViewport implements AlignViewportI,
     {
       colSel.hideColumns(start, end);
     }
+    isColSelChanged(true);
   }
 
   public void showColumn(int col)
   {
     colSel.revealHiddenColumns(col);
-
+    isColSelChanged(true);
   }
 
   public void showAllHiddenColumns()
   {
     colSel.revealAllHiddenColumns();
+    isColSelChanged(true);
   }
 
   // common hide/show seq stuff
@@ -1463,13 +1465,42 @@ public abstract class AlignmentViewport implements AlignViewportI,
 
   }
 
+  /**
+   * 
+   * @return null or the current reference sequence
+   */
+  public SequenceI getReferenceSeq()
+  {
+    return alignment.getSeqrep();
+  }
+
+  /**
+   * @param seq
+   * @return true iff seq is the reference for the alignment
+   */
+  public boolean isReferenceSeq(SequenceI seq)
+  {
+    return alignment.getSeqrep() == seq;
+  }
+
+  /**
+   * 
+   * @param seq
+   * @return true if there are sequences represented by this sequence that are
+   *         currently hidden
+   */
   public boolean isHiddenRepSequence(SequenceI seq)
   {
-    return alignment.getSeqrep() == seq
-            || (hiddenRepSequences != null && hiddenRepSequences
+    return (hiddenRepSequences != null && hiddenRepSequences
                     .containsKey(seq));
   }
 
+  /**
+   * 
+   * @param seq
+   * @return null or a sequence group containing the sequences that seq
+   *         represents
+   */
   public SequenceGroup getRepresentedSequences(SequenceI seq)
   {
     return (SequenceGroup) (hiddenRepSequences == null ? null
index 5b55e05..ec2c591 100644 (file)
@@ -68,6 +68,12 @@ public abstract class FeatureRendererModel implements
 
   protected AlignmentViewport av;
 
+  /*
+   * map holds per feature type, {{min, max}, {min, max}} feature score
+   * values for positional and non-positional features respectively
+   */
+  private Map<String, float[][]> minmax = new Hashtable<String, float[][]>();
+
   @Override
   public AlignViewportI getViewport()
   {
@@ -194,9 +200,7 @@ public abstract class FeatureRendererModel implements
     renderOrder = neworder;
   }
 
-  protected Hashtable minmax = new Hashtable();
-
-  public Hashtable getMinMax()
+  public Map<String, float[][]> getMinMax()
   {
     return minmax;
   }
@@ -210,7 +214,7 @@ public abstract class FeatureRendererModel implements
    */
   protected final byte[] normaliseScore(SequenceFeature sequenceFeature)
   {
-    float[] mm = ((float[][]) minmax.get(sequenceFeature.type))[0];
+    float[] mm = minmax.get(sequenceFeature.type)[0];
     final byte[] r = new byte[] { 0, (byte) 255 };
     if (mm != null)
     {
@@ -341,7 +345,7 @@ public abstract class FeatureRendererModel implements
     }
     if (minmax == null)
     {
-      minmax = new Hashtable();
+      minmax = new Hashtable<String, float[][]>();
     }
     AlignmentI alignment = av.getAlignment();
     for (int i = 0; i < alignment.getHeight(); i++)
@@ -396,7 +400,7 @@ public abstract class FeatureRendererModel implements
         if (!Float.isNaN(features[index].score))
         {
           int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
-          float[][] mm = (float[][]) minmax.get(features[index].getType());
+          float[][] mm = minmax.get(features[index].getType());
           if (mm == null)
           {
             mm = new float[][] { null, null };
@@ -557,31 +561,6 @@ public abstract class FeatureRendererModel implements
   }
 
   /**
-   * return a nominal colour for this feature
-   * 
-   * @param featureType
-   * @return standard color, or maximum colour for graduated colourscheme
-   */
-  public Color getColour(String featureType)
-  {
-    Object fc = getFeatureStyle(featureType);
-
-    if (fc instanceof Color)
-    {
-      return (Color) fc;
-    }
-    else
-    {
-      if (fc instanceof GraduatedColor)
-      {
-        return ((GraduatedColor) fc).getMaxColor();
-      }
-    }
-    throw new Error("Implementation Error: Unrecognised render object "
-            + fc.getClass() + " for features of type " + featureType);
-  }
-
-  /**
    * calculate the render colour for a specific feature using current feature
    * settings.
    * 
@@ -819,7 +798,7 @@ public abstract class FeatureRendererModel implements
     changeSupport.removePropertyChangeListener(listener);
   }
 
-  public Set getAllFeatureColours()
+  public Set<String> getAllFeatureColours()
   {
     return featureColours.keySet();
   }
@@ -834,6 +813,9 @@ public abstract class FeatureRendererModel implements
     return renderOrder != null;
   }
 
+  /**
+   * Returns feature types in ordering of rendering, where last means on top
+   */
   public List<String> getRenderOrder()
   {
     if (renderOrder == null)
@@ -943,7 +925,7 @@ public abstract class FeatureRendererModel implements
     while (en.hasNext())
     {
       String col = en.next();
-      fcols.put(col, getColour(col));
+      fcols.put(col, featureColours.get(col));
     }
     return fcols;
   }
index bca3145..48e3604 100644 (file)
@@ -46,7 +46,7 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
 
   protected AlignmentViewPanel ap;
 
-  protected List<AlignmentAnnotation> ourAnnots = null;
+  protected List<AlignmentAnnotation> ourAnnots;
 
   public AlignCalcWorker(AlignViewportI alignViewport,
           AlignmentViewPanel alignPanel)
@@ -68,17 +68,18 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
 
   }
 
+  @Override
   public boolean involves(AlignmentAnnotation i)
   {
     return ourAnnots != null && ourAnnots.contains(i);
   }
 
   /**
-   * permanently remove from the alignment all annotation rows managed by this
+   * Permanently removes from the alignment all annotation rows managed by this
    * worker
    */
   @Override
-  public void removeOurAnnotation()
+  public void removeAnnotation()
   {
     if (ourAnnots != null && alignViewport != null)
     {
@@ -90,6 +91,7 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI
           alignment.deleteAnnotation(aa, true);
         }
       }
+      ourAnnots.clear();
     }
   }
   // TODO: allow GUI to query workers associated with annotation to add items to
diff --git a/src/jalview/workers/AlignmentAnnotationFactory.java b/src/jalview/workers/AlignmentAnnotationFactory.java
new file mode 100644 (file)
index 0000000..37f3ca5
--- /dev/null
@@ -0,0 +1,117 @@
+package jalview.workers;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.Annotation;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+
+import java.awt.Color;
+
+/**
+ * Factory class with methods which allow clients (including external scripts
+ * such as Groovy) to 'register and forget' an alignment annotation calculator. <br>
+ * Currently supports two flavours of calculator:
+ * <ul>
+ * <li>a 'feature counter' which can count any desired property derivable from
+ * residue value and any sequence features at each position of the alignment</li>
+ * <li>a 'general purpose' calculator which computes one more complete
+ * AlignmentAnnotation objects</li>
+ * </ul>
+ */
+public class AlignmentAnnotationFactory
+{
+  /**
+   * Constructs and registers a new alignment annotation worker
+   * 
+   * @param counter
+   *          provider of feature counts per alignment position
+   */
+  public static void newCalculator(FeatureCounterI counter)
+  {
+    if (Desktop.getCurrentAlignFrame() != null)
+    {
+      newCalculator(Desktop.getCurrentAlignFrame(), counter);
+    }
+    else
+    {
+      System.err
+              .println("Can't register calculator as no alignment window has focus");
+    }
+  }
+
+  /**
+   * Constructs and registers a new alignment annotation worker
+   * 
+   * @param af
+   *          the AlignFrame for which the annotation is to be calculated
+   * @param counter
+   *          provider of feature counts per alignment position
+   */
+  public static void newCalculator(AlignFrame af, FeatureCounterI counter)
+  {
+    new ColumnCounterWorker(af, counter);
+  }
+
+  /**
+   * Constructs and registers a new alignment annotation worker
+   * 
+   * @param calculator
+   *          provider of AlignmentAnnotation for the alignment
+   */
+  public static void newCalculator(AnnotationProviderI calculator)
+  {
+    if (Desktop.getCurrentAlignFrame() != null)
+    {
+      newCalculator(Desktop.getCurrentAlignFrame(), calculator);
+    }
+    else
+    {
+      System.err
+              .println("Can't register calculator as no alignment window has focus");
+    }
+  }
+
+  /**
+   * Constructs and registers a new alignment annotation worker
+   * 
+   * @param af
+   *          the AlignFrame for which the annotation is to be calculated
+   * @param calculator
+   *          provider of AlignmentAnnotation for the alignment
+   */
+  public static void newCalculator(AlignFrame af,
+          AnnotationProviderI calculator)
+  {
+    new AnnotationWorker(af, calculator);
+  }
+
+  /**
+   * Factory method to construct an Annotation object
+   * 
+   * @param displayChar
+   * @param desc
+   * @param secondaryStructure
+   * @param val
+   * @param color
+   * @return
+   */
+  public static Annotation newAnnotation(String displayChar, String desc,
+          char secondaryStructure, float val, Color color)
+  {
+    return new Annotation(displayChar, desc, secondaryStructure, val, color);
+  }
+
+  /**
+   * Factory method to construct an AlignmentAnnotation object
+   * 
+   * @param name
+   * @param desc
+   * @param anns
+   * @return
+   */
+  public static AlignmentAnnotation newAlignmentAnnotation(String name,
+          String desc, Annotation[] anns)
+  {
+    return new AlignmentAnnotation(name, desc, anns);
+  }
+}
diff --git a/src/jalview/workers/AnnotationProviderI.java b/src/jalview/workers/AnnotationProviderI.java
new file mode 100644 (file)
index 0000000..653ff04
--- /dev/null
@@ -0,0 +1,17 @@
+package jalview.workers;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.gui.FeatureRenderer;
+
+import java.util.List;
+
+/**
+ * Interface to be satisfied by any class which computes one or more alignment
+ * annotations
+ */
+public interface AnnotationProviderI
+{
+  List<AlignmentAnnotation> calculateAnnotation(AlignmentI al,
+          FeatureRenderer fr);
+}
diff --git a/src/jalview/workers/AnnotationWorker.java b/src/jalview/workers/AnnotationWorker.java
new file mode 100644 (file)
index 0000000..fbf7531
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+package jalview.workers;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.FeatureRenderer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class to create and update one or more alignment annotations, given a
+ * 'calculator'.
+ * 
+ */
+class AnnotationWorker extends AlignCalcWorker
+{
+  /*
+   * the provider of the annotation calculations
+   */
+  AnnotationProviderI counter;
+
+  /**
+   * Constructor
+   * 
+   * @param af
+   * @param counter
+   */
+  public AnnotationWorker(AlignFrame af, AnnotationProviderI counter)
+  {
+    super(af.getViewport(), af.alignPanel);
+    ourAnnots = new ArrayList<AlignmentAnnotation>();
+    this.counter = counter;
+    calcMan.registerWorker(this);
+  }
+
+  @Override
+  public void run()
+  {
+    try
+    {
+      calcMan.notifyStart(this);
+
+      while (!calcMan.notifyWorking(this))
+      {
+        try
+        {
+          Thread.sleep(200);
+        } catch (InterruptedException ex)
+        {
+          ex.printStackTrace();
+        }
+      }
+      if (alignViewport.isClosed())
+      {
+        abortAndDestroy();
+        return;
+      }
+
+      removeAnnotations();
+      AlignmentI alignment = alignViewport.getAlignment();
+      if (alignment != null)
+      {
+        try
+        {
+          List<AlignmentAnnotation> anns = counter.calculateAnnotation(
+                  alignment, new FeatureRenderer((AlignmentPanel) ap));
+          for (AlignmentAnnotation ann : anns)
+          {
+            ann.showAllColLabels = true;
+            ann.graph = AlignmentAnnotation.BAR_GRAPH;
+            ourAnnots.add(ann);
+            alignment.addAnnotation(ann);
+          }
+        } catch (IndexOutOfBoundsException x)
+        {
+          // probable race condition. just finish and return without any fuss.
+          return;
+        }
+      }
+    } catch (OutOfMemoryError error)
+    {
+      ap.raiseOOMWarning("calculating annotations", error);
+      calcMan.workerCannotRun(this);
+    } finally
+    {
+      calcMan.workerComplete(this);
+    }
+
+    if (ap != null)
+    {
+      ap.adjustAnnotationHeight();
+      ap.paintAlignment(true);
+    }
+
+  }
+
+  /**
+   * Remove all our annotations before re-calculating them
+   */
+  void removeAnnotations()
+  {
+    for (AlignmentAnnotation ann : ourAnnots)
+    {
+      alignViewport.getAlignment().deleteAnnotation(ann);
+    }
+    ourAnnots.clear();
+  }
+
+  @Override
+  public void updateAnnotation()
+  {
+    // do nothing
+  }
+}
diff --git a/src/jalview/workers/ColumnCounterWorker.java b/src/jalview/workers/ColumnCounterWorker.java
new file mode 100644 (file)
index 0000000..6f4a4f3
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ */
+package jalview.workers;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignFrame;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.FeatureRenderer;
+import jalview.util.ColorUtils;
+import jalview.util.Comparison;
+
+import java.awt.Color;
+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>
+ * 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.
+ * 
+ */
+class ColumnCounterWorker extends AlignCalcWorker
+{
+  FeatureCounterI counter;
+
+  /**
+   * Constructor registers the annotation for the given alignment frame
+   * 
+   * @param af
+   * @param counter
+   */
+  public ColumnCounterWorker(AlignFrame af, FeatureCounterI counter)
+  {
+    super(af.getViewport(), af.alignPanel);
+    ourAnnots = new ArrayList<AlignmentAnnotation>();
+    this.counter = counter;
+    calcMan.registerWorker(this);
+  }
+
+  /**
+   * method called under control of AlignCalcManager to recompute the annotation
+   * when the alignment changes
+   */
+  @Override
+  public void run()
+  {
+    try
+    {
+      calcMan.notifyStart(this);
+
+      while (!calcMan.notifyWorking(this))
+      {
+        try
+        {
+          Thread.sleep(200);
+        } catch (InterruptedException ex)
+        {
+          ex.printStackTrace();
+        }
+      }
+      if (alignViewport.isClosed())
+      {
+        abortAndDestroy();
+        return;
+      }
+
+      removeAnnotation();
+      if (alignViewport.getAlignment() != null)
+      {
+        try
+        {
+          computeAnnotations();
+        } catch (IndexOutOfBoundsException x)
+        {
+          // probable race condition. just finish and return without any fuss.
+          return;
+        }
+      }
+    } catch (OutOfMemoryError error)
+    {
+      ap.raiseOOMWarning("calculating feature counts", error);
+      calcMan.workerCannotRun(this);
+    } finally
+    {
+      calcMan.workerComplete(this);
+    }
+
+    if (ap != null)
+    {
+      ap.adjustAnnotationHeight();
+      ap.paintAlignment(true);
+    }
+
+  }
+
+  /**
+   * 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.
+   */
+  void computeAnnotations()
+  {
+    FeatureRenderer fr = new FeatureRenderer((AlignmentPanel) ap);
+    // TODO use the commented out code once JAL-2075 is fixed
+    // to get adequate performance on genomic length sequence
+    AlignmentI alignment = alignViewport.getAlignment();
+    // AlignmentView alignmentView = alignViewport.getAlignmentView(false);
+    // AlignmentI alignment = alignmentView.getVisibleAlignment(' ');
+
+    // int width = alignmentView.getWidth();
+    int width = alignment.getWidth();
+    int height = alignment.getHeight();
+    int[] counts = new int[width];
+    int max = 0;
+
+    for (int col = 0; col < width; col++)
+    {
+      int count = 0;
+      for (int row = 0; row < height; row++)
+      {
+        count += countFeaturesAt(alignment, col, row, fr);
+      }
+      counts[col] = count;
+      max = Math.max(count, max);
+    }
+
+    Annotation[] anns = new Annotation[width];
+    /*
+     * add non-zero counts as annotations
+     */
+    for (int i = 0; i < counts.length; i++)
+    {
+      int count = counts[i];
+      if (count > 0)
+      {
+        Color color = ColorUtils.getGraduatedColour(count, 0, Color.cyan,
+                max, Color.blue);
+        anns[i] = new Annotation(String.valueOf(count),
+                String.valueOf(count), '0', count, color);
+      }
+    }
+
+    /*
+     * construct the annotation, save it and add it to the displayed alignment
+     */
+    AlignmentAnnotation ann = new AlignmentAnnotation(counter.getName(),
+            counter.getDescription(), anns);
+    ann.showAllColLabels = true;
+    ann.graph = AlignmentAnnotation.BAR_GRAPH;
+    ourAnnots.add(ann);
+    alignViewport.getAlignment().addAnnotation(ann);
+  }
+
+  /**
+   * Returns a count of any feature types present at the specified position of
+   * the alignment
+   * 
+   * @param alignment
+   * @param col
+   * @param row
+   * @param fr
+   */
+  int countFeaturesAt(AlignmentI alignment, int col, int row,
+          FeatureRenderer fr)
+  {
+    SequenceI seq = alignment.getSequenceAt(row);
+    if (seq == null)
+    {
+      return 0;
+    }
+    if (col >= seq.getLength())
+    {
+      return 0;// sequence doesn't extend this far
+    }
+    char res = seq.getCharAt(col);
+    if (Comparison.isGap(res))
+    {
+      return 0;
+    }
+    int pos = seq.findPosition(col);
+
+    /*
+     * compute a count for any displayed features at residue
+     */
+    // 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);
+    return count;
+  }
+
+  /**
+   * Method called when the user changes display options that may affect how the
+   * annotation is rendered, but do not change its values. Currently no such
+   * options affect user-defined annotation, so this method does nothing.
+   */
+  @Override
+  public void updateAnnotation()
+  {
+    // do nothing
+  }
+}
index a8698e6..14e2a31 100644 (file)
@@ -21,7 +21,6 @@
 package jalview.workers;
 
 import jalview.analysis.AAFrequency;
-import jalview.api.AlignCalcWorkerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.AlignmentAnnotation;
@@ -32,8 +31,7 @@ import jalview.schemes.ColourSchemeI;
 
 import java.util.Hashtable;
 
-public class ConsensusThread extends AlignCalcWorker implements
-        AlignCalcWorkerI
+public class ConsensusThread extends AlignCalcWorker
 {
   public ConsensusThread(AlignViewportI alignViewport,
           AlignmentViewPanel alignPanel)
index 236bccf..1075e4d 100644 (file)
@@ -21,7 +21,6 @@
 package jalview.workers;
 
 import jalview.analysis.Conservation;
-import jalview.api.AlignCalcWorkerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.AlignmentAnnotation;
@@ -30,8 +29,7 @@ import jalview.datamodel.AlignmentI;
 import java.util.ArrayList;
 import java.util.List;
 
-public class ConservationThread extends AlignCalcWorker implements
-        AlignCalcWorkerI
+public class ConservationThread extends AlignCalcWorker
 {
 
   private int ConsPercGaps = 25; // JBPNote : This should be a configurable
diff --git a/src/jalview/workers/FeatureCounterI.java b/src/jalview/workers/FeatureCounterI.java
new file mode 100644 (file)
index 0000000..aa4a283
--- /dev/null
@@ -0,0 +1,63 @@
+package jalview.workers;
+
+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
+ * 
+ */
+public interface FeatureCounterI
+{
+  /**
+   * Returns a count of some property 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>
+   * <li>1 if the residue is hydrophobic, else 0</li>
+   * <li>etc</li>
+   * </ul>
+   * 
+   * @param residue
+   *          the residue (or gap) at the position
+   * @param a
+   *          list of any sequence features which include the position
+   */
+  int count(String residue, List<SequenceFeature> features);
+
+  /**
+   * Returns a name for the annotation that this is counting, for use as the
+   * displayed label
+   * 
+   * @return
+   */
+  String getName();
+
+  /**
+   * Returns a description for the annotation, for display as a tooltip
+   * 
+   * @return
+   */
+  String getDescription();
+
+  /**
+   * Returns the colour (as [red, green, blue] values in the range 0-255) to use
+   * for the minimum value on histogram bars. If this is different to
+   * getMaxColour(), then bars will have a graduated colour.
+   * 
+   * @return
+   */
+  int[] getMinColour();
+
+  /**
+   * Returns the colour (as [red, green, blue] values in the range 0-255) to use
+   * for the maximum value on histogram bars. If this is the same as
+   * getMinColour(), then bars will have a single colour (not graduated).
+   * 
+   * @return
+   */
+  int[] getMaxColour();
+}
index e0b3833..3483dac 100644 (file)
@@ -21,7 +21,6 @@
 package jalview.workers;
 
 import jalview.analysis.StructureFrequency;
-import jalview.api.AlignCalcWorkerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.AlignmentAnnotation;
@@ -31,8 +30,7 @@ import jalview.datamodel.SequenceI;
 
 import java.util.Hashtable;
 
-public class StrucConsensusThread extends AlignCalcWorker implements
-        AlignCalcWorkerI
+public class StrucConsensusThread extends AlignCalcWorker
 {
   public StrucConsensusThread(AlignViewportI alignViewport,
           AlignmentViewPanel alignPanel)
index 40c88c1..3ba0e34 100644 (file)
@@ -141,7 +141,7 @@ public class DBRefFetcher implements Runnable
       String[] defdb = null, otherdb = sfetcher
               .getDbInstances(jalview.ws.dbsources.das.datamodel.DasSequenceSource.class);
       List<DbSourceProxy> selsources = new ArrayList<DbSourceProxy>();
-      Vector dasselsrc = (featureSettings != null) ? featureSettings
+      Vector<jalviewSourceI> dasselsrc = (featureSettings != null) ? featureSettings
               .getSelectedSources() : new jalview.gui.DasSourceBrowser()
               .getSelectedSources();
       Enumeration<jalviewSourceI> en = dasselsrc.elements();
@@ -190,6 +190,16 @@ public class DBRefFetcher implements Runnable
   }
 
   /**
+   * Constructor with only sequences provided
+   * 
+   * @param sequences
+   */
+  public DBRefFetcher(SequenceI[] sequences)
+  {
+    this(sequences, null, null, null, false);
+  }
+
+  /**
    * Add a listener to be notified when sequence fetching is complete
    * 
    * @param l
index d7ba24d..5f9b2d9 100644 (file)
@@ -22,11 +22,13 @@ package jalview.ws;
 
 import jalview.bin.Cache;
 import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
 import jalview.gui.FeatureSettings;
+import jalview.util.DBRefUtils;
 import jalview.util.MessageManager;
 import jalview.util.UrlLink;
 import jalview.ws.dbsources.das.api.DasSourceRegistryI;
@@ -186,8 +188,7 @@ public class DasSequenceFeatureFetcher
       {
         for (int j = 0; j < dbref.length; j++)
         {
-          if (dbref[j].getSource().equals(
-                  jalview.datamodel.DBRefSource.UNIPROT))
+          if (dbref[j].getSource().equals(DBRefSource.UNIPROT))
           {
             refCount++;
             break;
@@ -252,10 +253,10 @@ public class DasSequenceFeatureFetcher
     public void run()
     {
       running = true;
-      boolean isNuclueotide = af.getViewport().getAlignment()
+      boolean isNucleotide = af.getViewport().getAlignment()
               .isNucleotide();
-      new jalview.ws.DBRefFetcher(sequences, af, null, af.featureSettings,
-              isNuclueotide).fetchDBRefs(true);
+      new DBRefFetcher(sequences, af, null, af.featureSettings,
+              isNucleotide).fetchDBRefs(true);
 
       startFetching();
       setGuiFetchComplete();
@@ -286,7 +287,7 @@ public class DasSequenceFeatureFetcher
       {
         jalviewSourceI[] sources = sourceRegistry.getSources().toArray(
                 new jalviewSourceI[0]);
-        String active = jalview.bin.Cache.getDefault("DAS_ACTIVE_SOURCE",
+        String active = Cache.getDefault("DAS_ACTIVE_SOURCE",
                 "uniprot");
         StringTokenizer st = new StringTokenizer(active, "\t");
         selectedSources = new Vector();
@@ -643,10 +644,10 @@ public class DasSequenceFeatureFetcher
     {
       return null;
     }
-    DBRefEntry[] uprefs = jalview.util.DBRefUtils.selectRefs(
+    DBRefEntry[] uprefs = DBRefUtils.selectRefs(
             seq.getDBRefs(), new String[] {
             // jalview.datamodel.DBRefSource.PDB,
-            jalview.datamodel.DBRefSource.UNIPROT,
+            DBRefSource.UNIPROT,
             // jalview.datamodel.DBRefSource.EMBL - not tested on any EMBL coord
             // sys sources
             });
@@ -665,7 +666,7 @@ public class DasSequenceFeatureFetcher
 
         for (COORDINATES csys : dasSource.getVersion().getCOORDINATES())
         {
-          if (jalview.util.DBRefUtils.isDasCoordinateSystem(
+          if (DBRefUtils.isDasCoordinateSystem(
                   csys.getAuthority(), uprefs[j]))
           {
             debug("Launched fetcher for coordinate system "
index 1f8c28a..65179a2 100644 (file)
@@ -66,8 +66,6 @@ public class SequenceFetcher extends ASequenceFetcher
     addDBRefSourceImpl(Pdb.class);
     addDBRefSourceImpl(PfamFull.class);
     addDBRefSourceImpl(PfamSeed.class);
-    // ensures Seed alignment is 'default' for PFAM
-    addDBRefSourceImpl(RfamFull.class);
     addDBRefSourceImpl(RfamSeed.class);
     if (addDas)
     {
diff --git a/src/jalview/ws/dbsources/PDBRestClient.java b/src/jalview/ws/dbsources/PDBRestClient.java
deleted file mode 100644 (file)
index dbdb01d..0000000
+++ /dev/null
@@ -1,563 +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.
- */
-package jalview.ws.dbsources;
-
-import jalview.util.MessageManager;
-import jalview.ws.uimodel.PDBRestRequest;
-import jalview.ws.uimodel.PDBRestResponse;
-import jalview.ws.uimodel.PDBRestResponse.PDBResponseSummary;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.ws.rs.core.MediaType;
-
-import org.json.simple.JSONArray;
-import org.json.simple.JSONObject;
-import org.json.simple.parser.JSONParser;
-import org.json.simple.parser.ParseException;
-
-import com.sun.jersey.api.client.Client;
-import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.client.WebResource;
-import com.sun.jersey.api.client.config.ClientConfig;
-import com.sun.jersey.api.client.config.DefaultClientConfig;
-
-/**
- * A rest client for querying the Search endpoing of the PDB REST API
- * 
- * @author tcnofoegbu
- *
- */
-public class PDBRestClient
-{
-  public static final String PDB_SEARCH_ENDPOINT = "http://www.ebi.ac.uk/pdbe/search/pdb/select?";
-
-  private static int DEFAULT_RESPONSE_SIZE = 200;
-
-  /**
-   * Takes a PDBRestRequest object and returns a response upon execution
-   * 
-   * @param pdbRestRequest
-   *          the PDBRestRequest instance to be processed
-   * @return the pdbResponse object for the given request
-   * @throws Exception
-   */
-  public PDBRestResponse executeRequest(PDBRestRequest pdbRestRequest)
-          throws Exception
-  {
-    try
-    {
-      ClientConfig clientConfig = new DefaultClientConfig();
-      Client client = Client.create(clientConfig);
-
-      String wantedFields = getPDBDocFieldsAsCommaDelimitedString(pdbRestRequest
-              .getWantedFields());
-      int responseSize = (pdbRestRequest.getResponseSize() == 0) ? DEFAULT_RESPONSE_SIZE
-              : pdbRestRequest.getResponseSize();
-      String sortParam = null;
-      if (pdbRestRequest.getFieldToSortBy() == null
-              || pdbRestRequest.getFieldToSortBy().trim().isEmpty())
-      {
-        sortParam = "";
-      }
-      else
-      {
-        if (pdbRestRequest.getFieldToSortBy()
-                .equalsIgnoreCase("Resolution"))
-        {
-          sortParam = pdbRestRequest.getFieldToSortBy()
-                  + (pdbRestRequest.isAscending() ? " asc" : " desc");
-        }
-        else
-        {
-          sortParam = pdbRestRequest.getFieldToSortBy()
-                  + (pdbRestRequest.isAscending() ? " desc" : " asc");
-        }
-      }
-
-      String facetPivot = (pdbRestRequest.getFacetPivot() == null || pdbRestRequest
-              .getFacetPivot().isEmpty()) ? "" : pdbRestRequest
-              .getFacetPivot();
-      String facetPivotMinCount = String.valueOf(pdbRestRequest
-              .getFacetPivotMinCount());
-      
-      // Build request parameters for the REST Request
-      WebResource webResource = null;
-      if (pdbRestRequest.isFacet())
-      {
-        webResource = client.resource(PDB_SEARCH_ENDPOINT)
-                .queryParam("wt", "json").queryParam("fl", wantedFields)
-                .queryParam("rows", String.valueOf(responseSize))
-                .queryParam("q", pdbRestRequest.getQuery())
-                .queryParam("sort", sortParam).queryParam("facet", "true")
-                .queryParam("facet.pivot", facetPivot)
-                .queryParam("facet.pivot.mincount", facetPivotMinCount);
-      }
-      else
-      {
-        webResource = client.resource(PDB_SEARCH_ENDPOINT)
-                .queryParam("wt", "json").queryParam("fl", wantedFields)
-                .queryParam("rows", String.valueOf(responseSize))
-                .queryParam("q", pdbRestRequest.getQuery())
-                .queryParam("sort", sortParam);
-      }
-      // Execute the REST request
-      ClientResponse clientResponse = webResource.accept(
-              MediaType.APPLICATION_JSON).get(ClientResponse.class);
-
-      // Get the JSON string from the response object
-      String responseString = clientResponse.getEntity(String.class);
-      // System.out.println("query >>>>>>> " + pdbRestRequest.toString());
-
-      // Check the response status and report exception if one occurs
-      if (clientResponse.getStatus() != 200)
-      {
-        String errorMessage = "";
-        if (clientResponse.getStatus() == 400)
-        {
-          errorMessage = parseJsonExceptionString(responseString);
-          throw new Exception(errorMessage);
-        }
-        else
-        {
-          errorMessage = getMessageByHTTPStatusCode(clientResponse
-                  .getStatus());
-          throw new Exception(errorMessage);
-        }
-      }
-
-      // Make redundant objects eligible for garbage collection to conserve
-      // memory
-      clientResponse = null;
-      client = null;
-
-      // Process the response and return the result to the caller.
-      return parsePDBJsonResponse(responseString, pdbRestRequest);
-    } catch (Exception e)
-    {
-      String exceptionMsg = e.getMessage();
-      if (exceptionMsg.contains("SocketException"))
-      {
-        // No internet connection
-        throw new Exception(
-                MessageManager
-                        .getString("exception.unable_to_detect_internet_connection"));
-      }
-      else if (exceptionMsg.contains("UnknownHostException"))
-      {
-        // The server 'www.ebi.ac.uk' is unreachable
-        throw new Exception(
-                MessageManager
-                        .getString("exception.pdb_server_unreachable"));
-      }
-      else
-      {
-        throw e;
-      }
-    }
-  }
-
-  public String getMessageByHTTPStatusCode(int code)
-  {
-    String message = "";
-    switch (code)
-    {
-    case 410:
-      message = MessageManager
-              .getString("exception.pdb_rest_service_no_longer_available");
-      break;
-    case 403:
-    case 404:
-      message = MessageManager.getString("exception.resource_not_be_found");
-      break;
-    case 408:
-    case 409:
-    case 500:
-    case 501:
-    case 502:
-    case 503:
-    case 504:
-    case 505:
-      message = MessageManager.getString("exception.pdb_server_error");
-      break;
-
-    default:
-      break;
-    }
-    return message;
-  }
-
-  /**
-   * Process error response from PDB server if/when one occurs.
-   * 
-   * @param jsonResponse
-   *          the JSON string containing error message from the server
-   * @return the processed error message from the JSON string
-   */
-  public static String parseJsonExceptionString(String jsonErrorResponse)
-  {
-    StringBuilder errorMessage = new StringBuilder(
-            "\n============= PDB Rest Client RunTime error =============\n");
-
-    try
-    {
-      JSONParser jsonParser = new JSONParser();
-      JSONObject jsonObj = (JSONObject) jsonParser.parse(jsonErrorResponse);
-      JSONObject errorResponse = (JSONObject) jsonObj.get("error");
-
-      JSONObject responseHeader = (JSONObject) jsonObj
-              .get("responseHeader");
-      JSONObject paramsObj = (JSONObject) responseHeader.get("params");
-      String status = responseHeader.get("status").toString();
-      String message = errorResponse.get("msg").toString();
-      String query = paramsObj.get("q").toString();
-      String fl = paramsObj.get("fl").toString();
-
-      errorMessage.append("Status: ").append(status).append("\n");
-      errorMessage.append("Message: ").append(message).append("\n");
-      errorMessage.append("query: ").append(query).append("\n");
-      errorMessage.append("fl: ").append(fl).append("\n");
-
-    } catch (ParseException e)
-    {
-      e.printStackTrace();
-    }
-    return errorMessage.toString();
-  }
-
-  /**
-   * Parses the JSON response string from PDB REST API. The response is dynamic
-   * hence, only fields specifically requested for in the 'wantedFields'
-   * parameter is fetched/processed
-   * 
-   * @param pdbJsonResponseString
-   *          the JSON string to be parsed
-   * @param pdbRestRequest
-   *          the request object which contains parameters used to process the
-   *          JSON string
-   * @return
-   */
-  @SuppressWarnings("unchecked")
-  public static PDBRestResponse parsePDBJsonResponse(
-          String pdbJsonResponseString, PDBRestRequest pdbRestRequest)
-  {
-    PDBRestResponse searchResult = new PDBRestResponse();
-    List<PDBResponseSummary> result = null;
-    try
-    {
-      JSONParser jsonParser = new JSONParser();
-      JSONObject jsonObj = (JSONObject) jsonParser
-              .parse(pdbJsonResponseString);
-
-      JSONObject pdbResponse = (JSONObject) jsonObj.get("response");
-      String queryTime = ((JSONObject) jsonObj.get("responseHeader")).get(
-              "QTime").toString();
-      int numFound = Integer
-              .valueOf(pdbResponse.get("numFound").toString());
-      if (numFound > 0)
-      {
-        result = new ArrayList<PDBResponseSummary>();
-        JSONArray docs = (JSONArray) pdbResponse.get("docs");
-        for (Iterator<JSONObject> docIter = docs.iterator(); docIter
-                .hasNext();)
-        {
-          JSONObject doc = docIter.next();
-          result.add(searchResult.new PDBResponseSummary(doc,
-                  pdbRestRequest));
-        }
-        searchResult.setNumberOfItemsFound(numFound);
-        searchResult.setResponseTime(queryTime);
-        searchResult.setSearchSummary(result);
-      }
-    } catch (ParseException e)
-    {
-      e.printStackTrace();
-    }
-    return searchResult;
-  }
-
-  /**
-   * Takes a collection of PDBDocField and converts its 'code' Field values into
-   * a comma delimited string.
-   * 
-   * @param pdbDocfields
-   *          the collection of PDBDocField to process
-   * @return the comma delimited string from the pdbDocFields collection
-   */
-  public static String getPDBDocFieldsAsCommaDelimitedString(
-          Collection<PDBDocField> pdbDocfields)
-  {
-    String result = "";
-    if (pdbDocfields != null && !pdbDocfields.isEmpty())
-    {
-      StringBuilder returnedFields = new StringBuilder();
-      for (PDBDocField field : pdbDocfields)
-      {
-        returnedFields.append(",").append(field.getCode());
-      }
-      returnedFields.deleteCharAt(0);
-      result = returnedFields.toString();
-    }
-    return result;
-  }
-
-  /**
-   * Determines the column index for 'PDB Id' Fields in the dynamic summary
-   * table. The PDB Id serves as a unique identifier for a given row in the
-   * summary table
-   * 
-   * @param wantedFields
-   *          the available table columns in no particular order
-   * @return the pdb id field column index
-   */
-  public static int getPDBIdColumIndex(
-          Collection<PDBDocField> wantedFields, boolean hasRefSeq)
-  {
-
-    // If a reference sequence is attached then start counting from 1 else
-    // start from zero
-    int pdbFieldIndexCounter = hasRefSeq ? 1 : 0;
-
-    for (PDBDocField field : wantedFields)
-    {
-      if (field.equals(PDBDocField.PDB_ID))
-      {
-        break; // Once PDB Id index is determined exit iteration
-      }
-      ++pdbFieldIndexCounter;
-    }
-    return pdbFieldIndexCounter;
-  }
-
-  public static PDBDocField getPDBDocFieldByCode(String fieldCode)
-          throws Exception
-  {
-    for (PDBDocField curPDBDocField : PDBDocField.values())
-    {
-      if (curPDBDocField.getCode().equalsIgnoreCase(fieldCode))
-      {
-        return curPDBDocField;
-      }
-    }
-    throw new Exception("PDB doc Field not found!");
-  }
-
-  /**
-   * This enum represents the fields available in the PDB JSON response
-   *
-   */
-  public enum PDBDocField
-  {
-    PDB_ID("PDB Id", "pdb_id", Group.CROSS_REFS), TITLE(
-            "Title",
- "title", Group.MISCELLENOUS),
-    MOLECULE_NAME("Molecule",
-            "molecule_name",
-            Group.NAMES_AND_TAXONOMY), MOLECULE_TYPE(
-            "Molecule Type", "molecule_type", Group.NAMES_AND_TAXONOMY), MOLECULE_SEQUENCE(
-            "Sequence", "molecule_sequence", Group.MISCELLENOUS), PFAM_ACCESSION(
-            "PFAM Accession", "pfam_accession",
-            Group.CROSS_REFS), PFAM_NAME(
-            "PFAM Name", "pfam_name", Group.NAMES_AND_TAXONOMY), INTERPRO_NAME(
-            "InterPro Name", "interpro_name", Group.NAMES_AND_TAXONOMY), INTERPRO_ACCESSION(
-            "InterPro Accession", "interpro_accession",
-            Group.CROSS_REFS), UNIPROT_ID("UniProt Id",
-            "uniprot_id", Group.CROSS_REFS), UNIPROT_ACCESSION(
-            "UniProt Accession", "uniprot_accession",
-            Group.CROSS_REFS),
-
-    UNIPROT_COVERAGE(
-            "UniProt Coverage", "uniprot_coverage", Group.MISCELLENOUS), UNIPROT_FEATURES(
-            "Uniprot Features", "uniprot_features", Group.MISCELLENOUS), R_FACTOR(
-"R Factor",
-            "r_factor", Group.QUALITY_MEASURES), RESOLUTION("Resolution",
-            "resolution", Group.QUALITY_MEASURES), DATA_QUALITY(
-            "Data Quality", "data_quality", Group.QUALITY_MEASURES), OVERALL_QUALITY(
-            "Overall Quality", "overall_quality", Group.QUALITY_MEASURES), POLYMER_COUNT(
-            "Number of Polymers", "number_of_polymers", Group.MISCELLENOUS), PROTEIN_CHAIN_COUNT(
-            "Number of Protein Chains", "number_of_protein_chains",
-            Group.MISCELLENOUS), BOUND_MOLECULE_COUNT(
-            "Number of Bound Molecule", "number_of_bound_molecules",
-            Group.MISCELLENOUS), POLYMER_RESIDUE_COUNT(
-            "Number of Polymer Residue", "number_of_polymer_residues",
-            Group.MISCELLENOUS), GENUS("GENUS", "genus",
-            Group.NAMES_AND_TAXONOMY), GENE_NAME("Gene Name", "gene_name",
-            Group.NAMES_AND_TAXONOMY), EXPERIMENTAL_METHOD(
-            "Experimental Method", "experimental_method",
-            Group.PROCEDURE_AND_SOFTWARE), GO_ID("GO Id", "go_id",
-            Group.CROSS_REFS), ASSEMBLY_ID("Assembly Id",
-            "assembly_id", Group.CROSS_REFS), ASSEMBLY_FORM(
-            "Assembly Form", "assembly_form", Group.MISCELLENOUS), ASSEMBLY_TYPE(
-            "Assembly Type", "assembly_type", Group.MISCELLENOUS), SPACE_GROUP(
-            "Space Group", "spacegroup", Group.MISCELLENOUS), CATH_CODE(
-            "Cath Code", "cath_code", Group.CROSS_REFS), TAX_ID(
-            "Tax Id", "tax_id", Group.CROSS_REFS), TAX_QUERY(
-            "Tax Query", "tax_query", Group.CROSS_REFS), INTERACTING_ENTITY_ID(
-            "Interacting Entity Id", "interacting_entity_id",
-            Group.CROSS_REFS), INTERACTING_MOLECULES(
-            "Interacting Molecules", "interacting_molecules",
-            Group.MISCELLENOUS), PUBMED_ID("Pubmed Id", "pubmed_id",
-            Group.CROSS_REFS), STATUS("Status", "status",
-            Group.MISCELLENOUS), MODEL_QUALITY("Model Quality",
-            "model_quality", Group.QUALITY_MEASURES), PIVOT_RESOLUTION(
-            "Pivot Resolution", "pivot_resolution", Group.QUALITY_MEASURES), DATA_REDUCTION_SOFTWARE(
-            "Data reduction software", "data_reduction_software",
-            Group.PROCEDURE_AND_SOFTWARE), MAX_OBSERVED_RES(
-            "Max observed residues",
-            "max_observed_residues", Group.MISCELLENOUS), ORG_SCI_NAME(
-            "Organism scientific name", "organism_scientific_name",
-            Group.NAMES_AND_TAXONOMY), SUPER_KINGDOM("Super kingdom",
-            "superkingdom", Group.NAMES_AND_TAXONOMY), RANK("Rank", "rank",
-            Group.NAMES_AND_TAXONOMY), CRYSTALLISATION_PH(
-            "Crystallisation Ph",
-            "crystallisation_ph", Group.MISCELLENOUS), BIOLOGICAL_FUNCTION(
-            "Biological Function", "biological_function",
-            Group.MISCELLENOUS), BIOLOGICAL_PROCESS("Biological Process",
-            "biological_process", Group.MISCELLENOUS), BIOLOGICAL_CELL_COMPONENT(
-            "Biological Cell Component", "biological_cell_component",
-            Group.MISCELLENOUS), COMPOUND_NAME("Compound Name",
-            "compound_name", Group.NAMES_AND_TAXONOMY), COMPOUND_ID(
-            "Compound Id", "compound_id", Group.CROSS_REFS), COMPOUND_WEIGHT(
-            "Compound Weight", "compound_weight", Group.MISCELLENOUS), COMPOUND_SYSTEMATIC_NAME(
-            "Compound Systematic Name", "compound_systematic_name",
-            Group.NAMES_AND_TAXONOMY), INTERACTING_LIG(
-            "Interacting Ligands",
-            "interacting_ligands", Group.MISCELLENOUS), JOURNAL("Journal",
-            "journal", Group.MISCELLENOUS), ALL_AUTHORS("All Authors",
-            "all_authors", Group.MISCELLENOUS), EXPERIMENTAL_DATA_AVAILABLE(
-            "Experiment Data Available", "experiment_data_available",
-            Group.MISCELLENOUS), DIFFRACTION_PROTOCOL(
-            "Diffraction Protocol", "diffraction_protocol",
-            Group.PROCEDURE_AND_SOFTWARE), REFINEMENT_SOFTWARE(
-            "Refinement Software", "refinement_software",
-            Group.PROCEDURE_AND_SOFTWARE), STRUCTURE_DETERMINATION_METHOD(
-            "Structure Determination Method",
-            "structure_determination_method", Group.PROCEDURE_AND_SOFTWARE), SYNCHROTON_SITE(
-            "Synchrotron Site", "synchrotron_site", Group.MISCELLENOUS), SAMPLE_PREP_METHOD(
-            "Sample Preparation Method", "sample_preparation_method",
-            Group.PROCEDURE_AND_SOFTWARE), ENTRY_AUTHORS("Entry Authors",
-            "entry_authors", Group.MISCELLENOUS), CITATION_TITLE(
-            "Citation Title", "citation_title", Group.MISCELLENOUS), STRUCTURE_SOLUTION_SOFTWARE(
-            "Structure Solution Software", "structure_solution_software",
-            Group.PROCEDURE_AND_SOFTWARE), ENTRY_ENTITY("Entry Entity",
-            "entry_entity", Group.MISCELLENOUS), R_FREE("R Free", "r_free",
-            Group.QUALITY_MEASURES), NO_OF_POLYMER_ENTITIES(
-            "Number of Polymer Entities", "number_of_polymer_entities",
-            Group.MISCELLENOUS), NO_OF_BOUND_ENTITIES(
-            "Number of Bound Entities", "number_of_bound_entities",
-            Group.MISCELLENOUS), CRYSTALLISATION_RESERVOIR(
-            "Crystallisation Reservoir", "crystallisation_reservoir",
-            Group.MISCELLENOUS), DATA_SCALING_SW("Data Scalling Software",
-            "data_scaling_software", Group.PROCEDURE_AND_SOFTWARE), DETECTOR( 
-            "Detector", "detector", Group.MISCELLENOUS), DETECTOR_TYPE(
-            "Detector Type", "detector_type", Group.MISCELLENOUS), MODIFIED_RESIDUE_FLAG(
-            "Modified Residue Flag", "modified_residue_flag",
-            Group.MISCELLENOUS), NUMBER_OF_COPIES("Number of Copies",
-            "number_of_copies", Group.MISCELLENOUS), STRUCT_ASYM_ID(
-            "Struc Asym Id", "struct_asym_id",
-            Group.CROSS_REFS), HOMOLOGUS_PDB_ENTITY_ID(
-            "Homologus PDB Entity Id", "homologus_pdb_entity_id",
-            Group.CROSS_REFS), MOLECULE_SYNONYM(
-            "Molecule Synonym",
-            "molecule_synonym", Group.MISCELLENOUS), DEPOSITION_SITE(
-            "Deposition Site", "deposition_site", Group.MISCELLENOUS), SYNCHROTRON_BEAMLINE(
-            "Synchrotron Beamline", "synchrotron_beamline",
-            Group.MISCELLENOUS), ENTITY_ID("Entity Id", "entity_id",
-            Group.CROSS_REFS), BEAM_SOURCE_NAME(
-            "Beam Source Name",
- "beam_source_name",
-            Group.NAMES_AND_TAXONOMY), PROCESSING_SITE(
-            "Processing Site", "processing_site", Group.MISCELLENOUS), ENTITY_WEIGHT(
-            "Entity Weight", "entity_weight", Group.MISCELLENOUS), VERSION(
-            "Version", "_version_", Group.MISCELLENOUS), ALL("ALL", "text",
-            Group.MISCELLENOUS);
-
-    public enum Group
-    {
-      DATE_OF("Date Of", 5), NAMES_AND_TAXONOMY("Names & Taxonomy", 3),
-      MISCELLENOUS("Miscellenous", 6), QUALITY_MEASURES("Quality Measures",
-              1), CROSS_REFS("Cross References", 2),
-      PROCEDURE_AND_SOFTWARE("Procedures & Softwares", 4);
-
-      Group(String name, int sortOrder)
-      {
-        this.name = name;
-        this.sortOrder = sortOrder;
-      }
-
-      private String name;
-
-      private int sortOrder;
-
-      public String getName()
-      {
-        return this.name;
-      }
-
-      public int getSortOrder()
-      {
-        return sortOrder;
-      }
-
-      @Override
-      public String toString()
-      {
-        return this.name;
-      }
-    };
-    private String name;
-
-    private String code;
-
-    private Group group;
-
-    PDBDocField(String name, String code, Group group)
-    {
-      this.name = name;
-      this.code = code;
-      this.group = group;
-    }
-
-    public String getName()
-    {
-      return name;
-    }
-
-    public String getCode()
-    {
-      return code;
-    }
-
-    public Group getGroup()
-    {
-      return group;
-    }
-
-    @Override
-    public String toString()
-    {
-      return name;
-    }
-  }
-}
index 4a089f7..b2063c1 100644 (file)
@@ -53,7 +53,7 @@ public class Pdb extends EbiFileRetrievedProxy
 
   public static final String FEATURE_RES_NUM = "RESNUM";
 
-  private static String currentDefaultFomart = DBRefSource.PDB;
+  private static String currentDefaultFormat = DBRefSource.PDB;
 
   /*
    * (non-Javadoc)
@@ -132,11 +132,11 @@ public class Pdb extends EbiFileRetrievedProxy
       stopQuery();
       return null;
     }
-    String ext = getCurrentDefaultFomart().equalsIgnoreCase("mmcif") ? ".cif"
+    String ext = getCurrentDefaultFormat().equalsIgnoreCase("mmcif") ? ".cif"
             : ".xml";
     EBIFetchClient ebi = new EBIFetchClient();
     file = ebi.fetchDataAsFile("pdb:" + id,
-            getCurrentDefaultFomart().toLowerCase(), "raw", ext)
+            getCurrentDefaultFormat().toLowerCase(), "raw", ext)
             .getAbsolutePath();
     stopQuery();
     if (file == null)
@@ -148,7 +148,7 @@ public class Pdb extends EbiFileRetrievedProxy
 
       pdbAlignment = new FormatAdapter().readFile(file,
               jalview.io.AppletFormatAdapter.FILE,
-              getCurrentDefaultFomart());
+              getCurrentDefaultFormat());
       if (pdbAlignment != null)
       {
         List<SequenceI> toremove = new ArrayList<SequenceI>();
@@ -263,14 +263,14 @@ public class Pdb extends EbiFileRetrievedProxy
     return 0;
   }
 
-  public static String getCurrentDefaultFomart()
+  public static String getCurrentDefaultFormat()
   {
-    return currentDefaultFomart;
+    return currentDefaultFormat;
   }
 
-  public static void setCurrentDefaultFomart(String currentDefaultFomart)
+  public static void setCurrentDefaultFormat(String currentDefaultFomart)
   {
-    Pdb.currentDefaultFomart = currentDefaultFomart;
+    Pdb.currentDefaultFormat = currentDefaultFomart;
   }
 
   /**
index 4f5b8f5..62b9686 100644 (file)
@@ -40,7 +40,7 @@ public class PfamFull extends Pfam
   @Override
   protected String getXFAMURL()
   {
-    return "http://pfam.sanger.ac.uk/family/alignment/download/format?alnType=full&format=stockholm&order=t&case=l&gaps=default&entry=";
+    return "http://pfam.xfam.org/family/alignment/download/format?alnType=full&format=stockholm&order=t&case=l&gaps=default&entry=";
   }
 
   /*
index be8f044..053953c 100644 (file)
@@ -42,7 +42,7 @@ public class PfamSeed extends Pfam
   @Override
   protected String getXFAMURL()
   {
-    return "http://pfam.sanger.ac.uk/family/alignment/download/format?alnType=seed&format=stockholm&order=t&case=l&gaps=default&entry=";
+    return "http://pfam.xfam.org/family/alignment/download/format?alnType=seed&format=stockholm&order=t&case=l&gaps=default&entry=";
   }
 
   /*
index e1e9e9a..3053363 100644 (file)
@@ -42,7 +42,8 @@ public class RfamFull extends Rfam
   @Override
   protected String getXFAMURL()
   {
-    return "http://rfam.sanger.ac.uk/family/alignment/download/format?alnType=full&nseLabels=0&format=stockholm&acc=";
+    return "http://rfam.xfam.org/family/alignment/download/format?alnType=full&nseLabels=0&format=stockholm&acc=";
+
   }
 
   /*
index 2850fd5..77fb841 100644 (file)
@@ -42,11 +42,16 @@ public class RfamSeed extends Rfam
   @Override
   protected String getXFAMURL()
   {
-    return "http://rfam.sanger.ac.uk/family/alignment/download/format?alnType=seed&nseLabels=0&format=stockholm&acc=";
+    return "http://rfam.xfam.org/family/";
     // Janelia Farms url
     // "http://rfam.janelia.org/cgi-bin/getalignment?type=seed&fmt=stockholm&acc=";
   }
 
+  @Override
+  public String getXFAMURLSUFFIX()
+  {
+    return "/alignment";
+  }
   /*
    * (non-Javadoc)
    * 
index 26c9997..508047d 100644 (file)
@@ -55,7 +55,9 @@ public abstract class Xfam extends DbSourceProxyImpl
     startQuery();
     // TODO: trap HTTP 404 exceptions and return null
     AlignmentI rcds = new FormatAdapter().readFile(getXFAMURL()
-            + queries.trim().toUpperCase(), FormatAdapter.URL, "STH");
+            + queries.trim().toUpperCase() + getXFAMURLSUFFIX(),
+            jalview.io.FormatAdapter.URL,
+            "STH");
     for (int s = 0, sNum = rcds.getHeight(); s < sNum; s++)
     {
       rcds.getSequenceAt(s).addDBRef(new DBRefEntry(getXfamSource(),
@@ -81,4 +83,14 @@ public abstract class Xfam extends DbSourceProxyImpl
     return true;
   }
 
+  /**
+   * default suffix to append the retrieval URL for this source.
+   * 
+   * @return "" for most Xfam sources
+   */
+  public String getXFAMURLSUFFIX()
+  {
+    return "";
+  }
+
 }
index f929b1e..f4b1c31 100644 (file)
@@ -20,7 +20,6 @@
  */
 package jalview.ws.jws2;
 
-import jalview.api.AlignCalcWorkerI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.GraphLine;
@@ -46,8 +45,7 @@ import compbio.data.sequence.Score;
 import compbio.data.sequence.ScoreManager.ScoreHolder;
 import compbio.metadata.Argument;
 
-public class AADisorderClient extends JabawsCalcWorker implements
-        AlignCalcWorkerI
+public class AADisorderClient extends JabawsCalcWorker
 {
 
   private static final String THRESHOLD = "THRESHOLD";
index e4d6329..c15f256 100644 (file)
@@ -20,7 +20,6 @@
  */
 package jalview.ws.jws2;
 
-import jalview.api.AlignCalcWorkerI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.gui.AlignFrame;
@@ -43,9 +42,7 @@ import compbio.data.sequence.JpredAlignment;
 import compbio.metadata.Argument;
 
 public class JPred301Client extends JabawsMsaInterfaceAlignCalcWorker
-        implements AlignCalcWorkerI
 {
-
   /**
    * 
    * @return default args for this service when run as dynamic web service
@@ -87,6 +84,7 @@ public class JPred301Client extends JabawsMsaInterfaceAlignCalcWorker
     return (seqs.size() > 1);
   }
 
+  @Override
   public String getServiceActionText()
   {
     return "calculating consensus secondary structure prediction using JPred service";
@@ -112,6 +110,7 @@ public class JPred301Client extends JabawsMsaInterfaceAlignCalcWorker
    * update the consensus annotation from the sequence profile data using
    * current visualization settings.
    */
+  @Override
   public void updateResultAnnotation(boolean immediate)
   {
     if (immediate || !calcMan.isWorking(this) && msascoreset != null)
index c0b701b..8855d96 100644 (file)
@@ -256,11 +256,8 @@ public abstract class Jws2Client extends jalview.ws.WSClient
       });
       wsmenu.add(aaConEnabled);
       final JMenuItem modifyParams = new JMenuItem(aaui.getAAeditSettings());
-      modifyParams
-              .setToolTipText("<html><p>"
-                      + JvSwingUtils.wrapTooltip(false,
-                              aaui.getAAeditSettingsTooltip() + "</p>")
-                      + "</html>");
+      modifyParams.setToolTipText(JvSwingUtils.wrapTooltip(true,
+              aaui.getAAeditSettingsTooltip()));
       modifyParams.addActionListener(new ActionListener()
       {
 
index 47130a3..c83ef0f 100644 (file)
@@ -216,6 +216,7 @@ public class MsaWSClient extends Jws2Client
     return (WebServiceName.indexOf("lustal") > -1); // cheat!
   }
 
+  @Override
   public void attachWSMenuEntry(JMenu rmsawsmenu,
           final Jws2Instance service, final AlignFrame alignFrame)
   {
@@ -263,6 +264,7 @@ public class MsaWSClient extends Jws2Client
 
       method.addActionListener(new ActionListener()
       {
+        @Override
         public void actionPerformed(ActionEvent e)
         {
           AlignmentView msa = alignFrame.gatherSequencesForAlignment();
@@ -288,6 +290,7 @@ public class MsaWSClient extends Jws2Client
 
         method.addActionListener(new ActionListener()
         {
+          @Override
           public void actionPerformed(ActionEvent e)
           {
             AlignmentView msa = alignFrame.gatherSequencesForAlignment();
@@ -335,17 +338,19 @@ public class MsaWSClient extends Jws2Client
               }
 
             });
-            methodR.setToolTipText(JvSwingUtils.wrapTooltip(
+            String tooltip = JvSwingUtils.wrapTooltip(
                     true,
-                    "<p><strong>"
+                            "<strong>"
                             + (preset.isModifiable() ? MessageManager
                                     .getString("label.user_preset")
                                     : MessageManager
                                             .getString("label.service_preset"))
-                            + "</strong><br/>" + preset.getDescription()
-                            + "</p>"));
+                                    + "</strong><br/>"
+                                    + preset.getDescription());
+            methodR.setToolTipText(tooltip);
             methodR.addActionListener(new ActionListener()
             {
+              @Override
               public void actionPerformed(ActionEvent e)
               {
                 AlignmentView msa = alignFrame
index 41aa223..9ca6d2e 100644 (file)
@@ -20,7 +20,6 @@
  */
 package jalview.ws.jws2;
 
-import jalview.api.AlignCalcWorkerI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.gui.AlignFrame;
@@ -50,8 +49,7 @@ import compbio.metadata.Argument;
  * 
  */
 
-public class RNAalifoldClient extends JabawsCalcWorker implements
-        AlignCalcWorkerI
+public class RNAalifoldClient extends JabawsCalcWorker
 {
 
   String methodName;
@@ -75,6 +73,7 @@ public class RNAalifoldClient extends JabawsCalcWorker implements
     initViewportParams();
   }
 
+  @Override
   public String getCalcId()
   {
     return CALC_ID;
diff --git a/src/jalview/ws/uimodel/PDBRestResponse.java b/src/jalview/ws/uimodel/PDBRestResponse.java
deleted file mode 100644 (file)
index 3dc3563..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
- * Copyright (C) 2014 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.
- */
-
-package jalview.ws.uimodel;
-
-import jalview.datamodel.SequenceI;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-import jalview.ws.dbsources.PDBRestClient.PDBDocField.Group;
-
-import java.util.Collection;
-import java.util.Objects;
-
-import javax.swing.JTable;
-import javax.swing.table.DefaultTableModel;
-
-import org.json.simple.JSONObject;
-
-/**
- * Represents the response model produced by the PDBRestClient upon successful
- * execution of a given request
- * 
- * @author tcnofoegbu
- *
- */
-public class PDBRestResponse
-{
-  private int numberOfItemsFound;
-
-  private String responseTime;
-
-  private Collection<PDBResponseSummary> searchSummary;
-
-  public int getNumberOfItemsFound()
-  {
-    return numberOfItemsFound;
-  }
-
-  public void setNumberOfItemsFound(int itemFound)
-  {
-    this.numberOfItemsFound = itemFound;
-  }
-
-  public String getResponseTime()
-  {
-    return responseTime;
-  }
-
-  public void setResponseTime(String responseTime)
-  {
-    this.responseTime = responseTime;
-  }
-
-  public Collection<PDBResponseSummary> getSearchSummary()
-  {
-    return searchSummary;
-  }
-
-  public void setSearchSummary(Collection<PDBResponseSummary> searchSummary)
-  {
-    this.searchSummary = searchSummary;
-  }
-
-  /**
-   * Convenience method to obtain a Table model for a given summary List based
-   * on the request parameters
-   * 
-   * @param request
-   *          the PDBRestRequest object which holds useful information for
-   *          creating a table model
-   * @param summariesList
-   *          the summary list which contains the data for populating the
-   *          table's rows
-   * @return the table model which was dynamically generated
-   */
-  public static DefaultTableModel getTableModel(PDBRestRequest request,
-          Collection<PDBResponseSummary> summariesList)
-  {
-    final PDBDocField[] cols = request.getWantedFields().toArray(
-            new PDBDocField[0]);
-    final int colOffset = request.getAssociatedSequence() == null ? 0 : 1;
-    DefaultTableModel tableModel = new DefaultTableModel()
-    {
-      @Override
-      public boolean isCellEditable(int row, int column)
-      {
-        return false;
-      }
-
-      @Override
-      public Class<?> getColumnClass(int columnIndex)
-      {
-        if (colOffset == 1 && columnIndex == 0)
-        {
-          return String.class;
-        }
-        if (cols[columnIndex - colOffset].getGroup().getName()
-                .equalsIgnoreCase(Group.QUALITY_MEASURES.getName()))
-        {
-          return Double.class;
-        }
-        return String.class;
-      }
-
-    };
-    if (request.getAssociatedSequence() != null)
-    {
-      tableModel.addColumn("Ref Sequence"); // Create sequence column header if
-      // exists in the request
-    }
-    for (PDBDocField field : request.getWantedFields())
-    {
-      tableModel.addColumn(field.getName()); // Create sequence column header if
-                                             // exists in the request
-    }
-
-    for (PDBResponseSummary res : summariesList)
-    {
-      tableModel.addRow(res.getSummaryData()); // Populate table rows with
-                                               // summary list
-    }
-
-    return tableModel;
-  }
-
-  /**
-   * Model for a unique response summary
-   * 
-   */
-  public class PDBResponseSummary
-  {
-    private String pdbId;
-
-    private Object[] summaryRowData;
-
-    private SequenceI associatedSequence;
-
-    public PDBResponseSummary(JSONObject pdbJsonDoc, PDBRestRequest request)
-    {
-      Collection<PDBDocField> diplayFields = request.getWantedFields();
-      SequenceI associatedSeq = request.getAssociatedSequence();
-      int colCounter = 0;
-      summaryRowData = new Object[(associatedSeq != null) ? diplayFields
-              .size() + 1 : diplayFields.size()];
-      if (associatedSeq != null)
-      {
-        this.associatedSequence = associatedSeq;
-        summaryRowData[0] = associatedSequence;
-        colCounter = 1;
-      }
-
-      for (PDBDocField field : diplayFields)
-      {
-        String fieldData = (pdbJsonDoc.get(field.getCode()) == null) ? ""
-                : pdbJsonDoc.get(field.getCode()).toString();
-        if (field.equals(PDBDocField.PDB_ID))
-        {
-          this.pdbId = fieldData;
-          summaryRowData[colCounter++] = this.pdbId;
-        }
-        else
-        {
-          if (field.getGroup().getName()
-                  .equals(Group.QUALITY_MEASURES.getName()))
-          {
-            try
-            {
-              if (fieldData == null || fieldData.isEmpty())
-              {
-                summaryRowData[colCounter++] = null;
-              }
-              else
-              {
-              Double value = Double.valueOf(fieldData);
-              summaryRowData[colCounter++] = value;
-              }
-            } catch (Exception e)
-            {
-              e.printStackTrace();
-              System.out.println("offending value:" + fieldData);
-              summaryRowData[colCounter++] = 0.0;
-            }
-          }else{
-            summaryRowData[colCounter++] = (fieldData == null || fieldData
-                    .isEmpty()) ? null : fieldData;
-          }
-        }
-      }
-    }
-
-    public Object getPdbId()
-    {
-      return pdbId;
-    }
-
-    public void setPdbId(String pdbId)
-    {
-      this.pdbId = pdbId;
-    }
-
-    public Object[] getSummaryData()
-    {
-      return summaryRowData;
-    }
-
-    public void setSummaryData(Object[] summaryData)
-    {
-      this.summaryRowData = summaryData;
-    }
-
-    /**
-     * Returns a string representation of this object;
-     */
-    @Override
-    public String toString()
-    {
-      StringBuilder summaryFieldValues = new StringBuilder();
-      for (Object summaryField : summaryRowData)
-      {
-        summaryFieldValues.append(
-                summaryField == null ? " " : summaryField.toString())
-                .append("\t");
-      }
-      return summaryFieldValues.toString();
-    }
-
-    /**
-     * Returns hash code value for this object
-     */
-    @Override
-    public int hashCode()
-    {
-      return Objects.hash(this.pdbId, this.toString());
-    }
-
-    /**
-     * Indicates whether some object is equal to this one
-     */
-    @Override
-    public boolean equals(Object that)
-    {
-      if (!(that instanceof PDBResponseSummary))
-      {
-        return false;
-      }
-      PDBResponseSummary another = (PDBResponseSummary) that;
-      return this.toString().equals(another.toString());
-    }
-
-  }
-
-  public static void configureTableColumn(JTable tbl_summary,
-          Collection<PDBDocField> wantedFields)
-  {
-    for (PDBDocField wantedField : wantedFields)
-    {
-      try
-      {
-      if (wantedField.equals(PDBDocField.PDB_ID))
-      {
-        tbl_summary.getColumn(wantedField.getName()).setMinWidth(40);
-        tbl_summary.getColumn(wantedField.getName()).setMaxWidth(60);
-        tbl_summary.getColumn(wantedField.getName()).setPreferredWidth(45);
-      }
-      else if (wantedField.equals(PDBDocField.TITLE))
-      {
-        tbl_summary.getColumn(wantedField.getName()).setMinWidth(300);
-        tbl_summary.getColumn(wantedField.getName()).setMaxWidth(1000);
-        tbl_summary.getColumn(wantedField.getName()).setPreferredWidth(400);
-      }
-      else if (wantedField.getGroup() == Group.QUALITY_MEASURES)
-      {
-        tbl_summary.getColumn(wantedField.getName()).setMinWidth(50);
-        tbl_summary.getColumn(wantedField.getName()).setMaxWidth(150);
-        tbl_summary.getColumn(wantedField.getName()).setPreferredWidth(85);
-      }
-      else
-      {
-        tbl_summary.getColumn(wantedField.getName()).setMinWidth(50);
-        tbl_summary.getColumn(wantedField.getName()).setMaxWidth(400);
-        tbl_summary.getColumn(wantedField.getName()).setPreferredWidth(95);
-      }
-      } catch (Exception e)
-      {
-        e.printStackTrace();
-      }
-    }
-  }
-}
index 9255e5a..ff745ac 100644 (file)
@@ -32,6 +32,7 @@ import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.TaylorColourScheme;
+import jalview.structure.StructureViewSettings;
 
 import java.awt.Color;
 import java.util.Vector;
@@ -55,6 +56,7 @@ public class PDBChainTest
   public void setUp()
   {
     System.out.println("setup");
+    StructureViewSettings.setShowSeqFeatures(true);
     c = new PDBChain("1GAQ", "A");
   }
 
index 7ccbf97..2fc5325 100644 (file)
@@ -26,6 +26,7 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.analysis.AlignmentUtils.DnaVariant;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
@@ -1733,52 +1734,132 @@ public class AlignmentUtilsTests
     /*
      * first with no variants on dna
      */
-    LinkedHashMap<Integer, String[][]> variantsMap = AlignmentUtils
+    LinkedHashMap<Integer, List<DnaVariant>[]> variantsMap = AlignmentUtils
             .buildDnaVariantsMap(dna, map);
     assertTrue(variantsMap.isEmpty());
 
-    // single allele codon 1, on base 1
-    SequenceFeature sf = new SequenceFeature("sequence_variant", "", 1, 1,
+    /*
+     * single allele codon 1, on base 1
+     */
+    SequenceFeature sf1 = new SequenceFeature("sequence_variant", "", 1, 1,
+            0f, null);
+    sf1.setValue("alleles", "T");
+    sf1.setValue("ID", "sequence_variant:rs758803211");
+    dna.addSequenceFeature(sf1);
+
+    /*
+     * two alleles codon 2, on bases 2 and 3 (distinct variants)
+     */
+    SequenceFeature sf2 = new SequenceFeature("sequence_variant", "", 5, 5,
+            0f, null);
+    sf2.setValue("alleles", "T");
+    sf2.setValue("ID", "sequence_variant:rs758803212");
+    dna.addSequenceFeature(sf2);
+    SequenceFeature sf3 = new SequenceFeature("sequence_variant", "", 6, 6,
+            0f, null);
+    sf3.setValue("alleles", "G");
+    sf3.setValue("ID", "sequence_variant:rs758803213");
+    dna.addSequenceFeature(sf3);
+
+    /*
+     * two alleles codon 3, both on base 2 (one variant)
+     */
+    SequenceFeature sf4 = new SequenceFeature("sequence_variant", "", 8, 8,
             0f, null);
-    sf.setValue("alleles", "T");
-    dna.addSequenceFeature(sf);
-
-    // two alleles codon 2, on bases 2 and 3
-    sf = new SequenceFeature("sequence_variant", "", 5, 5, 0f, null);
-    sf.setValue("alleles", "T");
-    dna.addSequenceFeature(sf);
-    sf = new SequenceFeature("sequence_variant", "", 6, 6, 0f, null);
-    sf.setValue("alleles", "G");
-    dna.addSequenceFeature(sf);
-
-    // two alleles codon 3, both on base 2
-    sf = new SequenceFeature("sequence_variant", "", 8, 8, 0f, null);
-    sf.setValue("alleles", "C, G");
-    dna.addSequenceFeature(sf);
+    sf4.setValue("alleles", "C, G");
+    sf4.setValue("ID", "sequence_variant:rs758803214");
+    dna.addSequenceFeature(sf4);
 
     // no alleles on codon 4
-    // alleles on codon 5 on all 3 bases
-    sf = new SequenceFeature("sequence_variant", "", 13, 13, 0f, null);
-    sf.setValue("alleles", "C, G"); // (C duplicates given base value)
-    dna.addSequenceFeature(sf);
-    sf = new SequenceFeature("sequence_variant", "", 14, 14, 0f, null);
-    sf.setValue("alleles", "g, a"); // should force to upper-case
-    dna.addSequenceFeature(sf);
-    sf = new SequenceFeature("sequence_variant", "", 15, 15, 0f, null);
-    sf.setValue("alleles", "A, T");
-    dna.addSequenceFeature(sf);
 
+    /*
+     * alleles on codon 5 on all 3 bases (distinct variants)
+     */
+    SequenceFeature sf5 = new SequenceFeature("sequence_variant", "", 13,
+            13, 0f, null);
+    sf5.setValue("alleles", "C, G"); // (C duplicates given base value)
+    sf5.setValue("ID", "sequence_variant:rs758803215");
+    dna.addSequenceFeature(sf5);
+    SequenceFeature sf6 = new SequenceFeature("sequence_variant", "", 14,
+            14, 0f, null);
+    sf6.setValue("alleles", "g, a"); // should force to upper-case
+    sf6.setValue("ID", "sequence_variant:rs758803216");
+    dna.addSequenceFeature(sf6);
+    SequenceFeature sf7 = new SequenceFeature("sequence_variant", "", 15,
+            15, 0f, null);
+    sf7.setValue("alleles", "A, T");
+    sf7.setValue("ID", "sequence_variant:rs758803217");
+    dna.addSequenceFeature(sf7);
+
+    /*
+     * build map - expect variants on positions 1, 2, 3, 5
+     */
     variantsMap = AlignmentUtils.buildDnaVariantsMap(dna, map);
     assertEquals(4, variantsMap.size());
-    assertTrue(Arrays.deepEquals(new String[][] { { "A", "T" }, { "T" },
-        { "G" } }, variantsMap.get(1)));
-    assertTrue(Arrays.deepEquals(new String[][] { { "A" }, { "A", "T" },
-        { "A", "G" } }, variantsMap.get(2)));
-    assertTrue(Arrays.deepEquals(new String[][] { { "T" },
-        { "T", "C", "G" }, { "T" } }, variantsMap.get(3)));
-    // duplicated bases are not removed here, handled in computePeptideVariants
-    assertTrue(Arrays.deepEquals(new String[][] { { "C", "C", "G" },
-        { "C", "G", "A" }, { "C", "A", "T" } }, variantsMap.get(5)));
+
+    /*
+     * protein residue 1: variant on codon (ATG) base 1, not on 2 or 3
+     */
+    List<DnaVariant>[] pep1Variants = variantsMap.get(1);
+    assertEquals(3, pep1Variants.length);
+    assertEquals(1, pep1Variants[0].size());
+    assertEquals("A", pep1Variants[0].get(0).base); // codon[1] base
+    assertSame(sf1, pep1Variants[0].get(0).variant); // codon[1] variant
+    assertEquals(1, pep1Variants[1].size());
+    assertEquals("T", pep1Variants[1].get(0).base); // codon[2] base
+    assertNull(pep1Variants[1].get(0).variant); // no variant here
+    assertEquals(1, pep1Variants[2].size());
+    assertEquals("G", pep1Variants[2].get(0).base); // codon[3] base
+    assertNull(pep1Variants[2].get(0).variant); // no variant here
+
+    /*
+     * protein residue 2: variants on codon (AAA) bases 2 and 3
+     */
+    List<DnaVariant>[] pep2Variants = variantsMap.get(2);
+    assertEquals(3, pep2Variants.length);
+    assertEquals(1, pep2Variants[0].size());
+    // codon[1] base recorded while processing variant on codon[2]
+    assertEquals("A", pep2Variants[0].get(0).base);
+    assertNull(pep2Variants[0].get(0).variant); // no variant here
+    // codon[2] base and variant:
+    assertEquals(1, pep2Variants[1].size());
+    assertEquals("A", pep2Variants[1].get(0).base);
+    assertSame(sf2, pep2Variants[1].get(0).variant);
+    // codon[3] base was recorded when processing codon[2] variant
+    // and then the variant for codon[3] added to it
+    assertEquals(1, pep2Variants[2].size());
+    assertEquals("A", pep2Variants[2].get(0).base);
+    assertSame(sf3, pep2Variants[2].get(0).variant);
+
+    /*
+     * protein residue 3: variants on codon (TTT) base 2 only
+     */
+    List<DnaVariant>[] pep3Variants = variantsMap.get(3);
+    assertEquals(3, pep3Variants.length);
+    assertEquals(1, pep3Variants[0].size());
+    assertEquals("T", pep3Variants[0].get(0).base); // codon[1] base
+    assertNull(pep3Variants[0].get(0).variant); // no variant here
+    assertEquals(1, pep3Variants[1].size());
+    assertEquals("T", pep3Variants[1].get(0).base); // codon[2] base
+    assertSame(sf4, pep3Variants[1].get(0).variant); // codon[2] variant
+    assertEquals(1, pep3Variants[2].size());
+    assertEquals("T", pep3Variants[2].get(0).base); // codon[3] base
+    assertNull(pep3Variants[2].get(0).variant); // no variant here
+
+    /*
+     * three variants on protein position 5
+     */
+    List<DnaVariant>[] pep5Variants = variantsMap.get(5);
+    assertEquals(3, pep5Variants.length);
+    assertEquals(1, pep5Variants[0].size());
+    assertEquals("C", pep5Variants[0].get(0).base); // codon[1] base
+    assertSame(sf5, pep5Variants[0].get(0).variant); // codon[1] variant
+    assertEquals(1, pep5Variants[1].size());
+    assertEquals("C", pep5Variants[1].get(0).base); // codon[2] base
+    assertSame(sf6, pep5Variants[1].get(0).variant); // codon[2] variant
+    assertEquals(1, pep5Variants[2].size());
+    assertEquals("C", pep5Variants[2].get(0).base); // codon[3] base
+    assertSame(sf7, pep5Variants[2].get(0).variant); // codon[3] variant
   }
 
   /**
@@ -1788,67 +1869,164 @@ public class AlignmentUtilsTests
   @Test(groups = "Functional")
   public void testComputePeptideVariants()
   {
-    String[][] codonVariants = new String[][] { { "A" }, { "G" }, { "T" } };
-  
-    /*
-     * AGT codes for S - this is not included in the variants returned
-     */
-    List<String> variants = AlignmentUtils.computePeptideVariants(codonVariants, "S");
-    assertEquals("[]", variants.toString());
-  
-    // S is reported if it differs from the current value (A):
-    variants = AlignmentUtils.computePeptideVariants(codonVariants, "A");
-    assertEquals("[S]", variants.toString());
-  
     /*
-     * synonymous variant is not reported
+     * scenario: AAATTTCCC codes for KFP, with variants
+     *           GAA -> E
+     *           CAA -> Q
+     *           AAG synonymous
+     *           AAT -> N
+     *              TTC synonymous
+     *                 CAC,CGC -> H,R (as one variant)
      */
-    codonVariants = new String[][] { { "A" }, { "G" }, { "C", "T" } };
-    // AGC and AGT both code for S
-    variants = AlignmentUtils.computePeptideVariants(codonVariants, "s");
-    assertEquals("[]", variants.toString());
-  
+    SequenceI peptide = new Sequence("pep/10-12", "KFP");
+
     /*
-     * equivalent variants are only reported once
+     * two distinct variants for codon 1 position 1
+     * second one has clinical significance
      */
-    codonVariants = new String[][] { { "C" }, { "T" },
-        { "A", "C", "G", "T" } };
-    // CTA CTC CTG CTT all code for L
-    variants = AlignmentUtils.computePeptideVariants(codonVariants, "S");
-    assertEquals("[L]", variants.toString());
-  
+    SequenceFeature sf1 = new SequenceFeature("sequence_variant", "", 1, 1,
+            0f, null);
+    sf1.setValue("alleles", "A,G"); // GAA -> E
+    sf1.setValue("ID", "var1.125A>G");
+    SequenceFeature sf2 = new SequenceFeature("sequence_variant", "", 1, 1,
+            0f, null);
+    sf2.setValue("alleles", "A,C"); // CAA -> Q
+    sf2.setValue("ID", "var2");
+    sf2.setValue("clinical_significance", "Dodgy");
+    SequenceFeature sf3 = new SequenceFeature("sequence_variant", "", 3, 3,
+            0f, null);
+    sf3.setValue("alleles", "A,G"); // synonymous
+    sf3.setValue("ID", "var3");
+    sf3.setValue("clinical_significance", "None");
+    SequenceFeature sf4 = new SequenceFeature("sequence_variant", "", 3, 3,
+            0f, null);
+    sf4.setValue("alleles", "A,T"); // AAT -> N
+    sf4.setValue("ID", "sequence_variant:var4"); // prefix gets stripped off
+    sf4.setValue("clinical_significance", "Benign");
+    SequenceFeature sf5 = new SequenceFeature("sequence_variant", "", 6, 6,
+            0f, null);
+    sf5.setValue("alleles", "T,C"); // synonymous
+    sf5.setValue("ID", "var5");
+    sf5.setValue("clinical_significance", "Bad");
+    SequenceFeature sf6 = new SequenceFeature("sequence_variant", "", 8, 8,
+            0f, null);
+    sf6.setValue("alleles", "C,A,G"); // CAC,CGC -> H,R
+    sf6.setValue("ID", "var6");
+    sf6.setValue("clinical_significance", "Good");
+
+    List<DnaVariant> codon1Variants = new ArrayList<DnaVariant>();
+    List<DnaVariant> codon2Variants = new ArrayList<DnaVariant>();
+    List<DnaVariant> codon3Variants = new ArrayList<DnaVariant>();
+    List<DnaVariant> codonVariants[] = new ArrayList[3];
+    codonVariants[0] = codon1Variants;
+    codonVariants[1] = codon2Variants;
+    codonVariants[2] = codon3Variants;
+
     /*
-     * vary codons 1 and 2; variant products are sorted and non-redundant
+     * compute variants for protein position 1
      */
-    codonVariants = new String[][] { { "a", "C" }, { "g", "T" }, { "A" } };
-    // aga ata cga cta code for R, I, R, L
-    variants = AlignmentUtils.computePeptideVariants(codonVariants, "S");
-    assertEquals("[I, L, R]", variants.toString());
-  
+    codon1Variants.add(new DnaVariant("A", sf1));
+    codon1Variants.add(new DnaVariant("A", sf2));
+    codon2Variants.add(new DnaVariant("A"));
+    codon2Variants.add(new DnaVariant("A"));
+    codon3Variants.add(new DnaVariant("A", sf3));
+    codon3Variants.add(new DnaVariant("A", sf4));
+    AlignmentUtils.computePeptideVariants(peptide, 1, codonVariants);
+
     /*
-     * vary codons 2 and 3
+     * compute variants for protein position 2
      */
-    codonVariants = new String[][] { { "a" }, { "g", "T" }, { "A", "c" } };
-    // aga agc ata atc code for R, S, I, I
-    variants = AlignmentUtils.computePeptideVariants(codonVariants, "S");
-    assertEquals("[I, R]", variants.toString());
-  
+    codon1Variants.clear();
+    codon2Variants.clear();
+    codon3Variants.clear();
+    codon1Variants.add(new DnaVariant("T"));
+    codon2Variants.add(new DnaVariant("T"));
+    codon3Variants.add(new DnaVariant("T", sf5));
+    AlignmentUtils.computePeptideVariants(peptide, 2, codonVariants);
+
     /*
-     * vary codons 1 and 3
+     * compute variants for protein position 3
      */
-    codonVariants = new String[][] { { "a", "t" }, { "a" }, { "t", "g" } };
-    // aat aag tat tag code for N, K, Y, STOP - STOP sorted to end
-    variants = AlignmentUtils.computePeptideVariants(codonVariants, "S");
-    assertEquals("[K, N, Y, STOP]", variants.toString());
-  
+    codon1Variants.clear();
+    codon2Variants.clear();
+    codon3Variants.clear();
+    codon1Variants.add(new DnaVariant("C"));
+    codon2Variants.add(new DnaVariant("C", sf6));
+    codon3Variants.add(new DnaVariant("C"));
+    AlignmentUtils.computePeptideVariants(peptide, 3, codonVariants);
+
     /*
-     * vary codons 1, 2 and 3
+     * verify added sequence features for
+     * var1 K -> E
+     * var2 K -> Q
+     * var4 K -> N
+     * var6 P -> H
+     * var6 P -> R
      */
-    codonVariants = new String[][] { { "a", "t" }, { "G", "C" },
-        { "t", "g" } };
-    // agt agg act acg tgt tgg tct tcg code for S, R, T, T, C, W, S, S
-    variants = AlignmentUtils.computePeptideVariants(codonVariants, "S");
-    assertEquals("[C, R, T, W]", variants.toString());
+    SequenceFeature[] sfs = peptide.getSequenceFeatures();
+    assertEquals(5, sfs.length);
+    SequenceFeature sf = sfs[0];
+    assertEquals(1, sf.getBegin());
+    assertEquals(1, sf.getEnd());
+    assertEquals("p.Lys1Glu", sf.getDescription());
+    assertEquals("var1.125A>G", sf.getValue("ID"));
+    assertNull(sf.getValue("clinical_significance"));
+    assertEquals("ID=var1.125A>G", sf.getAttributes());
+    assertEquals(1, sf.links.size());
+    // link to variation is urlencoded
+    assertEquals(
+            "p.Lys1Glu var1.125A>G|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var1.125A%3EG",
+            sf.links.get(0));
+    assertEquals("Jalview", sf.getFeatureGroup());
+    sf = sfs[1];
+    assertEquals(1, sf.getBegin());
+    assertEquals(1, sf.getEnd());
+    assertEquals("p.Lys1Gln", sf.getDescription());
+    assertEquals("var2", sf.getValue("ID"));
+    assertEquals("Dodgy", sf.getValue("clinical_significance"));
+    assertEquals("ID=var2;clinical_significance=Dodgy", sf.getAttributes());
+    assertEquals(1, sf.links.size());
+    assertEquals(
+            "p.Lys1Gln var2|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var2",
+            sf.links.get(0));
+    assertEquals("Jalview", sf.getFeatureGroup());
+    sf = sfs[2];
+    assertEquals(1, sf.getBegin());
+    assertEquals(1, sf.getEnd());
+    assertEquals("p.Lys1Asn", sf.getDescription());
+    assertEquals("var4", sf.getValue("ID"));
+    assertEquals("Benign", sf.getValue("clinical_significance"));
+    assertEquals("ID=var4;clinical_significance=Benign", sf.getAttributes());
+    assertEquals(1, sf.links.size());
+    assertEquals(
+            "p.Lys1Asn var4|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var4",
+            sf.links.get(0));
+    assertEquals("Jalview", sf.getFeatureGroup());
+    sf = sfs[3];
+    assertEquals(3, sf.getBegin());
+    assertEquals(3, sf.getEnd());
+    assertEquals("p.Pro3His", sf.getDescription());
+    assertEquals("var6", sf.getValue("ID"));
+    assertEquals("Good", sf.getValue("clinical_significance"));
+    assertEquals("ID=var6;clinical_significance=Good", sf.getAttributes());
+    assertEquals(1, sf.links.size());
+    assertEquals(
+            "p.Pro3His var6|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var6",
+            sf.links.get(0));
+    // var5 generates two distinct protein variant features
+    assertEquals("Jalview", sf.getFeatureGroup());
+    sf = sfs[4];
+    assertEquals(3, sf.getBegin());
+    assertEquals(3, sf.getEnd());
+    assertEquals("p.Pro3Arg", sf.getDescription());
+    assertEquals("var6", sf.getValue("ID"));
+    assertEquals("Good", sf.getValue("clinical_significance"));
+    assertEquals("ID=var6;clinical_significance=Good", sf.getAttributes());
+    assertEquals(1, sf.links.size());
+    assertEquals(
+            "p.Pro3Arg var6|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var6",
+            sf.links.get(0));
+    assertEquals("Jalview", sf.getFeatureGroup());
   }
 
   /**
diff --git a/test/jalview/analysis/SeqsetUtilsTest.java b/test/jalview/analysis/SeqsetUtilsTest.java
new file mode 100644 (file)
index 0000000..b4d079a
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+package jalview.analysis;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+
+import java.util.Hashtable;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * @author jprocter
+ *
+ */
+public class SeqsetUtilsTest
+{
+  /**
+   * test for JAL-2046 bug - duplication of sequence features on reconstructed
+   * alignment
+   */
+  @Test(groups = { "Functional" })
+  public void testSeqFeatureAddition()
+  {
+    SequenceI[] sqset = new SequenceI[] {
+        new Sequence("Aseq1", "AREALSEQ"),
+        new Sequence("Aseq2", "AREALSEQ") };
+
+    AlignmentI al = new Alignment(sqset);
+    al.setDataset(null);
+    AlignmentI ds = al.getDataset();
+    SequenceFeature sf1 = new SequenceFeature("f1", "foo", "bleh", 2, 3,
+            "far"), sf2 = new SequenceFeature("f2", "foo", "bleh", 2, 3,
+            "far");
+    ds.getSequenceAt(0).addSequenceFeature(sf1);
+    Hashtable unq = SeqsetUtils.uniquify(sqset, true);
+    SequenceI[] sqset2 = new SequenceI[] {
+        new Sequence(sqset[0].getName(), sqset[0].getSequenceAsString()),
+        new Sequence(sqset[1].getName(), sqset[1].getSequenceAsString()) };
+    Assert.assertTrue(sqset[0].getSequenceFeatures()[0] == sf1);
+    Assert.assertEquals(sqset2[0].getSequenceFeatures(), null);
+    ds.getSequenceAt(0).addSequenceFeature(sf2);
+    Assert.assertEquals(sqset[0].getSequenceFeatures().length, 2);
+    SeqsetUtils.deuniquify(unq, sqset2);
+    // explicitly test that original sequence features still exist because they
+    // are on the shared dataset sequence
+    Assert.assertEquals(sqset[0].getSequenceFeatures().length, 2);
+    Assert.assertEquals(sqset2[0].getSequenceFeatures().length, 2);
+    Assert.assertTrue(sqset[0].getSequenceFeatures()[0] == sqset2[0]
+            .getSequenceFeatures()[0]);
+    Assert.assertTrue(sqset[0].getSequenceFeatures()[1] == sqset2[0]
+            .getSequenceFeatures()[1]);
+  }
+}
index 2dc2ac3..029483f 100644 (file)
@@ -27,7 +27,7 @@ import jalview.gui.AlignFrame;
 import jalview.io.FileLoader;
 import jalview.io.FormatAdapter;
 
-import org.testng.AssertJUnit;
+import org.testng.Assert;
 import org.testng.annotations.Test;
 
 public class FeatureScoreModelTest
@@ -40,14 +40,13 @@ public class FeatureScoreModelTest
 
   int[] sf3 = new int[] { -1, -1, -1, -1, -1, -1, 76, 77 };
 
-  @Test(groups = { "Functional" })
-  public void testFeatureScoreModel() throws Exception
+  public AlignFrame getTestAlignmentFrame()
   {
     AlignFrame alf = new FileLoader(false).LoadFileWaitTillLoaded(
             alntestFile, FormatAdapter.PASTE);
     AlignmentI al = alf.getViewport().getAlignment();
-    AssertJUnit.assertEquals(4, al.getHeight());
-    AssertJUnit.assertEquals(5, al.getWidth());
+    Assert.assertEquals(al.getHeight(), 4);
+    Assert.assertEquals(al.getWidth(), 5);
     for (int i = 0; i < 4; i++)
     {
       SequenceI ds = al.getSequenceAt(i).getDatasetSequence();
@@ -72,21 +71,68 @@ public class FeatureScoreModelTest
     alf.getFeatureRenderer().setVisible("sf2");
     alf.getFeatureRenderer().setVisible("sf3");
     alf.getFeatureRenderer().findAllFeatures(true);
-    AssertJUnit.assertEquals("Number of feature types", 3, alf
-            .getFeatureRenderer().getDisplayedFeatureTypes().size());
-    AssertJUnit.assertTrue(alf.getCurrentView().areFeaturesDisplayed());
+    Assert.assertEquals(alf.getFeatureRenderer().getDisplayedFeatureTypes()
+            .size(), 3, "Number of feature types");
+    Assert.assertTrue(alf.getCurrentView().areFeaturesDisplayed());
+    return alf;
+  }
+
+  @Test(groups = { "Functional" })
+  public void testFeatureScoreModel() throws Exception
+  {
+    AlignFrame alf = getTestAlignmentFrame();
     FeatureScoreModel fsm = new FeatureScoreModel();
-    AssertJUnit.assertTrue(fsm.configureFromAlignmentView(alf
+    Assert.assertTrue(fsm.configureFromAlignmentView(alf
             .getCurrentView().getAlignPanel()));
     alf.selectAllSequenceMenuItem_actionPerformed(null);
     float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView(
             true));
-    AssertJUnit.assertTrue("FER1_MESCR should be identical with RAPSA (2)",
-            dm[0][2] == 0f);
-    AssertJUnit
-            .assertTrue(
-                    "FER1_MESCR should be further from SPIOL (1) than it is from RAPSA (2)",
-                    dm[0][1] > dm[0][2]);
+    Assert.assertTrue(dm[0][2] == 0f,
+            "FER1_MESCR (0) should be identical with RAPSA (2)");
+    Assert.assertTrue(dm[0][1] > dm[0][2],
+            "FER1_MESCR (0) should be further from SPIOL (1) than it is from RAPSA (2)");
+  }
+
+  @Test(groups = { "Functional" })
+  public void testFeatureScoreModel_hiddenFirstColumn() throws Exception
+  {
+    AlignFrame alf = getTestAlignmentFrame();
+    // hiding first two columns shouldn't affect the tree
+    alf.getViewport().hideColumns(0, 1);
+    FeatureScoreModel fsm = new FeatureScoreModel();
+    Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
+            .getAlignPanel()));
+    alf.selectAllSequenceMenuItem_actionPerformed(null);
+    float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView(
+            true));
+    Assert.assertTrue(dm[0][2] == 0f,
+            "FER1_MESCR (0) should be identical with RAPSA (2)");
+    Assert.assertTrue(dm[0][1] > dm[0][2],
+            "FER1_MESCR (0) should be further from SPIOL (1) than it is from RAPSA (2)");
+  }
 
+  @Test(groups = { "Functional" })
+  public void testFeatureScoreModel_HiddenColumns() throws Exception
+  {
+    AlignFrame alf = getTestAlignmentFrame();
+    // hide columns and check tree changes
+    alf.getViewport().hideColumns(3, 4);
+    alf.getViewport().hideColumns(0, 1);
+    FeatureScoreModel fsm = new FeatureScoreModel();
+    Assert.assertTrue(fsm.configureFromAlignmentView(alf.getCurrentView()
+            .getAlignPanel()));
+    alf.selectAllSequenceMenuItem_actionPerformed(null);
+    float[][] dm = fsm.findDistances(alf.getViewport().getAlignmentView(
+            true));
+    Assert.assertTrue(dm[0][2] == 0f,
+            "After hiding last two columns FER1_MESCR (0) should still be identical with RAPSA (2)");
+    Assert.assertTrue(dm[0][1] == 0f,
+            "After hiding last two columns FER1_MESCR (0) should now also be identical with SPIOL (1)");
+    for (int s=0;s<3;s++)
+    {
+      Assert.assertTrue(dm[s][3] > 0f, "After hiding last two columns "
+              + alf.getViewport().getAlignment().getSequenceAt(s).getName()
+              + "(" + s + ") should still be distinct from FER1_MAIZE (3)");
+    }
   }
 }
index bd445c4..5a45176 100644 (file)
@@ -32,6 +32,7 @@ import jalview.io.FormatAdapter;
 import jalview.util.MapList;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
@@ -474,4 +475,33 @@ public class AlignmentTest
     align.addCodonFrame(acf2);
     assertTrue(align.getDataset().getCodonFrames().contains(acf));
   }
+
+  @Test(groups = "Functional")
+  public void getVisibleStartAndEndIndexTest()
+  {
+    Sequence seq = new Sequence("testSeq", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+    AlignmentI align = new Alignment(new SequenceI[] { seq });
+    ArrayList<int[]> hiddenCols = new ArrayList<int[]>();
+
+    int[] startEnd = align.getVisibleStartAndEndIndex(hiddenCols);
+    assertEquals(0, startEnd[0]);
+    assertEquals(25, startEnd[1]);
+
+    hiddenCols.add(new int[] { 0, 0 });
+    startEnd = align.getVisibleStartAndEndIndex(hiddenCols);
+    assertEquals(1, startEnd[0]);
+    assertEquals(25, startEnd[1]);
+
+    hiddenCols.add(new int[] { 6, 9 });
+    hiddenCols.add(new int[] { 11, 12 });
+    startEnd = align.getVisibleStartAndEndIndex(hiddenCols);
+    assertEquals(1, startEnd[0]);
+    assertEquals(25, startEnd[1]);
+
+    hiddenCols.add(new int[] { 24, 25 });
+    startEnd = align.getVisibleStartAndEndIndex(hiddenCols);
+    System.out.println(startEnd[0] + " : " + startEnd[1]);
+    assertEquals(1, startEnd[0]);
+    assertEquals(23, startEnd[1]);
+  }
 }
index 0245b15..e1d04eb 100644 (file)
@@ -93,6 +93,83 @@ public class ColumnSelectionTest
     assertEquals(2, cs.findColumnPosition(5));
   }
 
+  /**
+   * Test the code used to locate the reference sequence ruler origin
+   */
+  @Test(groups = { "Functional" })
+  public void testLocateVisibleBoundsofSequence()
+  {
+    ColumnSelection cs = new ColumnSelection();
+    SequenceI seq = new Sequence("RefSeq", "-A-SD-ASD--E---");
+    assertEquals(2, seq.findIndex(seq.getStart()));
+
+    // no hidden columns
+    assertEquals(
+            Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
+                seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
+                seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
+                seq.findIndex(seq.getEnd()) - 1 }),
+            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
+
+    // hidden column on gap after end of sequence - should not affect bounds
+    cs.hideColumns(13);
+    assertEquals(
+            Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 1,
+                seq.findIndex(seq.getEnd()) - 1, seq.getStart(),
+                seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
+                seq.findIndex(seq.getEnd()) - 1 }),
+            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
+
+    cs.revealAllHiddenColumns();
+    // hidden column on gap before beginning of sequence - should vis bounds by
+    // one
+    cs.hideColumns(0);
+    assertEquals(
+            Arrays.toString(new int[] { seq.findIndex(seq.getStart()) - 2,
+                seq.findIndex(seq.getEnd()) - 2, seq.getStart(),
+                seq.getEnd(), seq.findIndex(seq.getStart()) - 1,
+                seq.findIndex(seq.getEnd()) - 1 }),
+            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
+
+    cs.revealAllHiddenColumns();
+    // hide columns around most of sequence - leave one residue remaining
+    cs.hideColumns(1, 3);
+    cs.hideColumns(6, 11);
+    assertEquals("-D",
+            cs.getVisibleSequenceStrings(0, 5, new SequenceI[] { seq })[0]);
+    assertEquals(
+            Arrays.toString(new int[] { 1, 1, 3, 3,
+                seq.findIndex(seq.getStart()) - 1,
+                seq.findIndex(seq.getEnd()) - 1 }),
+            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
+    cs.revealAllHiddenColumns();
+
+    // hide whole sequence - should just get location of hidden region
+    // containing sequence
+    cs.hideColumns(1, 11);
+    assertEquals(
+            Arrays.toString(new int[] { 0, 1, 0, 0,
+                seq.findIndex(seq.getStart()) - 1,
+                seq.findIndex(seq.getEnd()) - 1 }),
+            Arrays.toString(cs.locateVisibleBoundsOfSequence(seq)));
+
+  }
+
+  @Test(groups={"Functional"})
+  public void testLocateVisibleBoundsPathologicals()
+  {
+    // test some pathological cases we missed
+    AlignmentI al = new Alignment(new SequenceI[] { new Sequence("refseqGaptest","KTDVTI----------NFI-----G----L")});
+    ColumnSelection cs = new ColumnSelection();
+    cs.hideInsertionsFor(al.getSequenceAt(0));
+    assertEquals(
+            "G",
+            ""
+                    + al.getSequenceAt(0).getCharAt(
+                            cs.adjustForHiddenColumns(9)));
+
+
+  }
   @Test(groups = { "Functional" })
   public void testHideColumns()
   {
@@ -401,4 +478,47 @@ public class ColumnSelectionTest
     cs.addElement(0);
     assertEquals(0, cs.getMin());
   }
+
+  @Test(groups = { "Functional" })
+  public void testEquals()
+  {
+    ColumnSelection cs = new ColumnSelection();
+    cs.addElement(0);
+    cs.addElement(513);
+    cs.addElement(1);
+    cs.hideColumns(3);
+    cs.hideColumns(7);
+    cs.hideColumns(5,9);
+
+    // same selections added in a different order
+    ColumnSelection cs2 = new ColumnSelection();
+    cs2.addElement(1);
+    cs2.addElement(513);
+    cs2.addElement(0);
+    cs2.hideColumns(6, 9);
+    cs2.hideColumns(5, 8);
+    cs2.hideColumns(3);
+    
+    assertTrue(cs.equals(cs2));
+    assertTrue(cs.equals(cs));
+    assertTrue(cs2.equals(cs));
+    assertTrue(cs2.equals(cs2));
+
+    cs2.addElement(12);
+    assertFalse(cs.equals(cs2));
+    assertFalse(cs2.equals(cs));
+
+    cs2.removeElement(12);
+    assertTrue(cs.equals(cs2));
+
+    cs2.hideColumns(88);
+    assertFalse(cs.equals(cs2));
+    /*
+     * unhiding a column adds it to selection!
+     */
+    cs2.revealHiddenColumns(88);
+    assertFalse(cs.equals(cs2));
+    cs.addElement(88);
+    assertTrue(cs.equals(cs2));
+  }
 }
index 705c773..8d3c878 100644 (file)
@@ -23,6 +23,8 @@ package jalview.datamodel;
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 
+import jalview.util.Comparison;
+
 import org.testng.annotations.Test;
 
 /**
@@ -30,6 +32,29 @@ import org.testng.annotations.Test;
  */
 public class SeqCigarTest
 {
+  @Test(groups = { "Functional" })
+  public void testFindPosition()
+  {
+    SequenceI oseq = new Sequence("MySeq", "ASD---ASD---ASD", 37, 45);
+    oseq.createDatasetSequence();
+    SeqCigar cs = new SeqCigar(oseq);
+    assertEquals(oseq.getSequenceAsString(), cs.getSequenceString('-'));
+    for (int c = 0, cLen = oseq.getLength(); c < cLen; c++)
+    {
+      int os_p = oseq.findPosition(c);
+      int cigar_p = cs.findPosition(c);
+      if (Comparison.isGap(oseq.getCharAt(c)))
+      {
+        assertEquals("Expected gap at position " + os_p + " column " + c,
+                -1, cigar_p);
+      }
+      else
+      {
+        assertEquals("Positions don't match for at column " + c, os_p,
+                cigar_p);
+      }
+    }
+  }
   /*
    * refactored 'as is' from main method
    * 
index 95755ee..03c4a96 100644 (file)
@@ -30,10 +30,12 @@ import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
 
 import jalview.datamodel.PDBEntry.Type;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Vector;
 
+import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -310,9 +312,13 @@ public class SequenceTest
     assertEquals(1, sfs.length);
     assertSame(sf, sfs[0]);
 
+
     /*
      * SequenceFeature on sequence and dataset sequence; returns that on
      * sequence
+     * 
+     * Note JAL-2046: spurious: we have no use case for this at the moment.
+     * This test also buggy - as sf2.equals(sf), no new feature is added
      */
     SequenceFeature sf2 = new SequenceFeature();
     sq.getDatasetSequence().addSequenceFeature(sf2);
@@ -322,17 +328,20 @@ public class SequenceTest
 
     /*
      * SequenceFeature on dataset sequence only
+     * Note JAL-2046: spurious: we have no use case for setting a non-dataset sequence's feature array to null at the moment.
      */
     sq.setSequenceFeatures(null);
-    sfs = sq.getSequenceFeatures();
-    assertEquals(1, sfs.length);
-    assertSame(sf2, sfs[0]);
+    assertNull(sq.getDatasetSequence().getSequenceFeatures());
 
     /*
      * Corrupt case - no SequenceFeature, dataset's dataset is the original
      * sequence. Test shows no infinite loop results.
      */
     sq.getDatasetSequence().setSequenceFeatures(null);
+    /**
+     * is there a usecase for this ? setDatasetSequence should throw an error if
+     * this actually occurs.
+     */
     sq.getDatasetSequence().setDatasetSequence(sq); // loop!
     assertNull(sq.getSequenceFeatures());
   }
@@ -390,14 +399,78 @@ public class SequenceTest
     sq.setStart(3);
     sq.setEnd(4);
 
+    sq.setDescription("Test sequence description..");
+    sq.setVamsasId("TestVamsasId");
+    sq.setSourceDBRef(new DBRefEntry("PDB", "version0", "1TST"));
+
+    sq.addDBRef(new DBRefEntry("PDB", "version1", "1Tst"));
+    sq.addDBRef(new DBRefEntry("PDB", "version2", "2Tst"));
+    sq.addDBRef(new DBRefEntry("PDB", "version3", "3Tst"));
+    sq.addDBRef(new DBRefEntry("PDB", "version4", "4Tst"));
+
+    sq.addPDBId(new PDBEntry("1PDB", "A", Type.PDB, "filePath/test1"));
+    sq.addPDBId(new PDBEntry("1PDB", "B", Type.PDB, "filePath/test1"));
+    sq.addPDBId(new PDBEntry("2PDB", "A", Type.MMCIF, "filePath/test2"));
+    sq.addPDBId(new PDBEntry("2PDB", "B", Type.MMCIF, "filePath/test2"));
+
+    sq.getDatasetSequence().addDBRef(
+            new DBRefEntry("PDB", "version1", "1Tst"));
+    sq.getDatasetSequence().addDBRef(
+            new DBRefEntry("PDB", "version2", "2Tst"));
+    sq.getDatasetSequence().addDBRef(
+            new DBRefEntry("PDB", "version3", "3Tst"));
+    sq.getDatasetSequence().addDBRef(
+            new DBRefEntry("PDB", "version4", "4Tst"));
+
+    sq.getDatasetSequence().addPDBId(
+            new PDBEntry("1PDB", "A", Type.PDB, "filePath/test1"));
+    sq.getDatasetSequence().addPDBId(
+            new PDBEntry("1PDB", "B", Type.PDB, "filePath/test1"));
+    sq.getDatasetSequence().addPDBId(
+            new PDBEntry("2PDB", "A", Type.MMCIF, "filePath/test2"));
+    sq.getDatasetSequence().addPDBId(
+            new PDBEntry("2PDB", "B", Type.MMCIF, "filePath/test2"));
+
+    ArrayList<Annotation> annotsList = new ArrayList<Annotation>();
+    System.out.println(">>>>>> " + sq.getSequenceAsString().length());
+    annotsList.add(new Annotation("A", "A", 'X', 0.1f));
+    annotsList.add(new Annotation("A", "A", 'X', 0.1f));
+    Annotation[] annots = annotsList.toArray(new Annotation[0]);
+    sq.addAlignmentAnnotation(new AlignmentAnnotation("Test annot",
+            "Test annot description", annots));
+    sq.getDatasetSequence().addAlignmentAnnotation(
+            new AlignmentAnnotation("Test annot", "Test annot description",
+                    annots));
+    Assert.assertEquals(sq.getDescription(), "Test sequence description..");
+    Assert.assertEquals(sq.getDBRefs().length, 4);
+    Assert.assertEquals(sq.getAllPDBEntries().size(), 4);
+    Assert.assertNotNull(sq.getAnnotation());
+    Assert.assertEquals(sq.getAnnotation()[0].annotations.length, 2);
+    Assert.assertEquals(sq.getDatasetSequence().getDBRefs().length, 4);
+    Assert.assertEquals(sq.getDatasetSequence().getAllPDBEntries().size(),
+            4);
+    Assert.assertNotNull(sq.getDatasetSequence().getAnnotation());
+
     Sequence derived = (Sequence) sq.deriveSequence();
+
+    Assert.assertEquals(derived.getDescription(),
+            "Test sequence description..");
+    Assert.assertEquals(derived.getDBRefs().length, 4);
+    Assert.assertEquals(derived.getAllPDBEntries().size(), 4);
+    Assert.assertNotNull(derived.getAnnotation());
+    Assert.assertEquals(derived.getAnnotation()[0].annotations.length, 2);
+    Assert.assertEquals(derived.getDatasetSequence().getDBRefs().length, 4);
+    Assert.assertEquals(derived.getDatasetSequence().getAllPDBEntries()
+            .size(), 4);
+    Assert.assertNotNull(derived.getDatasetSequence().getAnnotation());
+
     assertEquals("CD", derived.getSequenceAsString());
     assertSame(sq.getDatasetSequence(), derived.getDatasetSequence());
 
     assertNull(sq.sequenceFeatures);
-    // assertNull(derived.sequenceFeatures);
+    assertNull(derived.sequenceFeatures);
+    // derived sequence should access dataset sequence features
     assertNotNull(sq.getSequenceFeatures());
-    // derived sequence has a copy of the sequence features (is this right?)
     assertArrayEquals(sq.getSequenceFeatures(),
             derived.getSequenceFeatures());
   }
@@ -471,6 +544,8 @@ public class SequenceTest
     seq1.setDescription("description");
     seq1.addAlignmentAnnotation(new AlignmentAnnotation("label", "desc",
             1.3d));
+    // JAL-2046 - what is the contract for using a derived sequence's
+    // addSequenceFeature ?
     seq1.addSequenceFeature(new SequenceFeature("type", "desc", 22, 33,
             12.4f, "group"));
     seq1.addPDBId(new PDBEntry("1A70", "B", Type.PDB, "File"));
@@ -518,7 +593,11 @@ public class SequenceTest
     // copy has a copy of the sequence feature:
     SequenceFeature[] sfs = copy.getSequenceFeatures();
     assertEquals(1, sfs.length);
-    assertFalse(sfs[0] == seq1.getSequenceFeatures()[0]);
+    if (seq1.getDatasetSequence()!=null && copy.getDatasetSequence()==seq1.getDatasetSequence()) {
+      assertTrue(sfs[0] == seq1.getSequenceFeatures()[0]);
+    } else {
+      assertFalse(sfs[0] == seq1.getSequenceFeatures()[0]);
+    }
     assertTrue(sfs[0].equals(seq1.getSequenceFeatures()[0]));
 
     // copy has a copy of the PDB entry
index 6f7c1ad..56e1339 100644 (file)
@@ -12,7 +12,7 @@ public class EnsemblRestClientTest
 {
 
   @Test(suiteName = "live")
-  public void testLiveCheckEnsembl()
+  public void testIsEnsemblAvailable()
   {
     EnsemblRestClient sf = new EnsemblRestClient()
     {
index 6df479c..6ce8a3b 100644 (file)
@@ -160,61 +160,6 @@ public class EnsemblSeqProxyTest
     }
   }
 
-  @Test(suiteName = "live")
-  public void testLiveCheckEnsembl()
-  {
-    EnsemblRestClient sf = new EnsemblRestClient()
-    {
-
-      @Override
-      public String getDbName()
-      {
-        // TODO Auto-generated method stub
-        return null;
-      }
-
-      @Override
-      public AlignmentI getSequenceRecords(String queries) throws Exception
-      {
-        // TODO Auto-generated method stub
-        return null;
-      }
-
-      @Override
-      protected URL getUrl(List<String> ids) throws MalformedURLException
-      {
-        // TODO Auto-generated method stub
-        return null;
-      }
-
-      @Override
-      protected boolean useGetRequest()
-      {
-        // TODO Auto-generated method stub
-        return false;
-      }
-
-      @Override
-      protected String getRequestMimeType(boolean b)
-      {
-        // TODO Auto-generated method stub
-        return null;
-      }
-
-      @Override
-      protected String getResponseMimeType()
-      {
-        // TODO Auto-generated method stub
-        return null;
-      }
-
-    };
-    boolean isAvailable = sf.isEnsemblAvailable();
-    System.out.println("Ensembl is "
-            + (isAvailable ? "UP!"
-                    : "DOWN or unreachable ******************* BAD!"));
-  }
-
   @Test(groups = "Functional")
   public void getGenomicRangesFromFeatures()
   {
@@ -267,7 +212,8 @@ public class EnsemblSeqProxyTest
     sb = new StringBuilder();
     EnsemblSeqProxy.reverseComplementAllele(sb, "-GATt"); // revcomp=aATC-
     EnsemblSeqProxy.reverseComplementAllele(sb, "hgmd_mutation");
-    assertEquals("aATC-,hgmd_mutation", sb.toString());
+    EnsemblSeqProxy.reverseComplementAllele(sb, "PhenCode_variation");
+    assertEquals("aATC-,hgmd_mutation,PhenCode_variation", sb.toString());
   }
 
   /**
index cde4afe..1dc9b8d 100644 (file)
@@ -21,7 +21,7 @@ public class EnsemblXrefTest
            "{\"primary_id\":\"GO:0000165\",\"dbname\":\"GO\"}]";
   //@formatter:on
 
-  @Test(groups = "functional")
+  @Test(groups = "Functional")
   public void testGetCrossReferences()
   {
     System.out.println(JSON);
index a68f7c8..7ac1579 100644 (file)
@@ -50,7 +50,8 @@ public class JmolParserTest
    * 1QCF is the full PDB file including headers, HETATM etc
    */
   String[] testFile = new String[] { "./examples/1GAQ.txt",
-      "./test/jalview/ext/jmol/1xyz.pdb" };
+      "./test/jalview/ext/jmol/1xyz.pdb",
+      "./test/jalview/ext/jmol/1qcf.pdb" };
 
   //@formatter:off
   // a modified and very cut-down extract of 4UJ4
diff --git a/test/jalview/fts/core/FTSRestClientTest.java b/test/jalview/fts/core/FTSRestClientTest.java
new file mode 100644 (file)
index 0000000..ddf60bc
--- /dev/null
@@ -0,0 +1,300 @@
+package jalview.fts.core;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class FTSRestClientTest
+{
+  private FTSRestClient ftsRestClient;
+
+  @BeforeMethod(alwaysRun = true)
+  public void setup()
+  {
+    ftsRestClient = new FTSRestClient()
+    {
+
+      @Override
+      public String getColumnDataConfigFileName()
+      {
+        return "/fts/uniprot_data_columns.txt";
+      }
+
+      @Override
+      public FTSRestResponse executeRequest(FTSRestRequest ftsRequest)
+              throws Exception
+      {
+        return null;
+      }
+    };
+  }
+
+  @Test(groups = { "Functional" })
+  public void getPrimaryKeyColumIndexTest()
+  {
+    Collection<FTSDataColumnI> wantedFields = ftsRestClient
+            .getAllDefaulDisplayedDataColumns();
+    int foundIndex = -1;
+    try
+    {
+      Assert.assertEquals(foundIndex, -1);
+      foundIndex = ftsRestClient.getPrimaryKeyColumIndex(wantedFields,
+              false);
+      Assert.assertEquals(foundIndex, 0);
+      foundIndex = ftsRestClient
+              .getPrimaryKeyColumIndex(wantedFields, true);
+      Assert.assertEquals(foundIndex, 1);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getAllDefaulDisplayedDataColumns()
+  {
+    Assert.assertNotNull(ftsRestClient.getAllDefaulDisplayedDataColumns());
+    Assert.assertTrue(!ftsRestClient.getAllDefaulDisplayedDataColumns()
+            .isEmpty());
+    Assert.assertEquals(ftsRestClient.getAllDefaulDisplayedDataColumns()
+            .size(), 7);
+  }
+
+  @Test(groups = { "Functional" })
+  public void getDataColumnsFieldsAsCommaDelimitedString()
+  {
+    Collection<FTSDataColumnI> wantedFields = ftsRestClient
+            .getAllDefaulDisplayedDataColumns();
+    String actual = ftsRestClient
+            .getDataColumnsFieldsAsCommaDelimitedString(wantedFields);
+    Assert.assertEquals(actual,
+            "id,entry name,protein names,genes,organism,reviewed,length");
+  }
+
+
+  @Test(groups = { "Functional" })
+  public void getAllFTSDataColumns()
+  {
+    Collection<FTSDataColumnI> allFields = ftsRestClient
+            .getAllFTSDataColumns();
+    Assert.assertNotNull(allFields);
+    Assert.assertEquals(allFields.size(), 117);
+  }
+
+  @Test(groups = { "Functional" })
+  public void getSearchableDataColumns()
+  {
+    Collection<FTSDataColumnI> searchalbeFields = ftsRestClient
+            .getSearchableDataColumns();
+    Assert.assertNotNull(searchalbeFields);
+    Assert.assertEquals(searchalbeFields.size(), 30);
+  }
+
+  @Test(groups = { "Functional" })
+  public void getPrimaryKeyColumn()
+  {
+    FTSDataColumnI expectedPKColumn;
+    try
+    {
+      expectedPKColumn = ftsRestClient
+              .getDataColumnByNameOrCode("Uniprot Id");
+      Assert.assertNotNull(ftsRestClient.getPrimaryKeyColumn());
+      Assert.assertEquals(ftsRestClient.getPrimaryKeyColumn(),
+              expectedPKColumn);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getDataColumnByNameOrCode()
+  {
+    try
+    {
+      FTSDataColumnI foundDataCol = ftsRestClient
+              .getDataColumnByNameOrCode("genes");
+      Assert.assertNotNull(foundDataCol);
+      Assert.assertEquals(foundDataCol.getName(), "Gene Names");
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getDataColumnGroupById()
+  {
+    FTSDataColumnGroupI foundDataColGroup;
+    try
+    {
+      foundDataColGroup = ftsRestClient.getDataColumnGroupById("g3");
+      Assert.assertNotNull(foundDataColGroup);
+      Assert.assertEquals(foundDataColGroup.getName(), "Names & Taxonomy");
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getDefaultResponsePageSize()
+  {
+    int defaultResSize = ftsRestClient.getDefaultResponsePageSize();
+    Assert.assertEquals(defaultResSize, 500);
+  }
+
+  @Test(groups = { "Functional" })
+  public void getColumnMinWidthTest()
+  {
+    try
+    {
+      FTSDataColumnI foundDataCol = ftsRestClient
+              .getDataColumnByNameOrCode("Protein names");
+      Assert.assertNotNull(foundDataCol);
+      int actualColMinWidth = foundDataCol.getMinWidth();
+      Assert.assertEquals(actualColMinWidth, 300);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getColumnMaxWidthTest()
+  {
+    try
+    {
+      FTSDataColumnI foundDataCol = ftsRestClient
+              .getDataColumnByNameOrCode("Protein names");
+      Assert.assertNotNull(foundDataCol);
+      int actualColMinWidth = foundDataCol.getMaxWidth();
+      Assert.assertEquals(actualColMinWidth, 1500);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getColumnPreferredWidthTest()
+  {
+    try
+    {
+      FTSDataColumnI foundDataCol = ftsRestClient
+              .getDataColumnByNameOrCode("Protein names");
+      Assert.assertNotNull(foundDataCol);
+      int actualColMinWidth = foundDataCol.getPreferredWidth();
+      Assert.assertEquals(actualColMinWidth, 500);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getColumnClassTest()
+  {
+    try
+    {
+      FTSDataColumnI foundDataCol = ftsRestClient
+              .getDataColumnByNameOrCode("Protein names");
+      Assert.assertNotNull(foundDataCol);
+      Assert.assertEquals(foundDataCol.getDataType().getDataTypeClass(),
+              String.class);
+      foundDataCol = ftsRestClient.getDataColumnByNameOrCode("length");
+      Assert.assertNotNull(foundDataCol);
+      Assert.assertEquals(foundDataCol.getDataType().getDataTypeClass(),
+              Integer.class);
+      // foundDataCol = ftsRestClient.getDataColumnByNameOrCode("length");
+      // Assert.assertNotNull(foundDataCol);
+      // Assert.assertEquals(foundDataCol.getDataColumnClass(), Double.class);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void coverageForEqualsAndHashFunction()
+  {
+    Set<FTSDataColumnI> uniqueSet = new HashSet<FTSDataColumnI>();
+    Collection<FTSDataColumnI> searchableCols = ftsRestClient
+            .getSearchableDataColumns();
+    for (FTSDataColumnI foundCol : searchableCols)
+    {
+      uniqueSet.add(foundCol);
+      uniqueSet.add(foundCol);
+    }
+    Assert.assertTrue(!uniqueSet.isEmpty());
+    Assert.assertEquals(uniqueSet.size(), 30);
+  }
+
+  @Test(groups = { "Functional" })
+  public void coverageForMiscellaneousBranches()
+  {
+    String actual = ftsRestClient.getPrimaryKeyColumn().toString();
+    Assert.assertEquals(actual, "Uniprot Id");
+
+    String actualGroupStr;
+    try
+    {
+      actualGroupStr = ftsRestClient.getDataColumnGroupById("g4")
+              .toString();
+      Assert.assertEquals(actualGroupStr, "Procedures & Softwares");
+      actualGroupStr = ftsRestClient.getDataColumnGroupById(
+              "unavailable group").toString();
+    } catch (Exception e)
+    {
+      Assert.assertTrue(true);
+    }
+
+    String actualResourseFile = ftsRestClient
+            .getResourceFile("/fts/uniprot_data_columns.txt");
+    Assert.assertNotNull(actualResourseFile);
+    Assert.assertTrue(actualResourseFile.length() > 31);
+  }
+
+  @Test(groups = { "Functional" }, expectedExceptions = Exception.class)
+  public void coverageForExceptionBranches() throws Exception
+  {
+    try
+    {
+      ftsRestClient.getDataColumnByNameOrCode("unavailable column");
+    } catch (Exception e)
+    {
+      System.out.println(e.getMessage());
+      String expectedMessage = "Couldn't find data column with name : unavailable column";
+      Assert.assertEquals(e.getMessage(), expectedMessage);
+    }
+    try
+    {
+      ftsRestClient.getDataColumnGroupById("unavailable column group Id");
+    } catch (Exception e)
+    {
+      System.out.println(e.getMessage());
+      String expectedMessage = "Couldn't find data column group with id : unavailable column group Id";
+      Assert.assertEquals(e.getMessage(), expectedMessage);
+    }
+
+    ftsRestClient.getDataColumnByNameOrCode("unavailable column");
+
+    ftsRestClient.getResourceFile("unavailable resource file");
+
+  }
+
+}
similarity index 79%
rename from test/jalview/gui/PDBSearchPanelTest.java
rename to test/jalview/fts/service/pdb/PDBFTSPanelTest.java
index 5e31bef..ac1d304 100644 (file)
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.gui;
+package jalview.fts.service.pdb;
 
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.fts.service.pdb.PDBFTSPanel;
+
 import javax.swing.JInternalFrame;
 import javax.swing.JTextField;
 
@@ -30,7 +32,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-public class PDBSearchPanelTest
+public class PDBFTSPanelTest
 {
 
   @BeforeMethod(alwaysRun = true)
@@ -46,7 +48,7 @@ public class PDBSearchPanelTest
   @Test(groups = { "Functional" })
   public void populateCmbSearchTargetOptionsTest()
   {
-    PDBSearchPanel searchPanel = new PDBSearchPanel(null);
+    PDBFTSPanel searchPanel = new PDBFTSPanel(null);
     assertTrue(searchPanel.getCmbSearchTarget().getItemCount() > 0);
     searchPanel.populateCmbSearchTargetOptions();
   }
@@ -55,13 +57,13 @@ public class PDBSearchPanelTest
   public void testDecodeSearchTerm()
   {
     String expectedString = "1xyz OR text:2xyz OR text:3xyz";
-    String outcome = PDBSearchPanel.decodeSearchTerm("1xyz:A;2xyz;3xyz",
+    String outcome = PDBFTSPanel.decodeSearchTerm("1xyz:A;2xyz;3xyz",
             "text");
     // System.out.println("1 >>>>>>>>>>> " + outcome);
     assertEquals(expectedString, outcome);
 
     expectedString = "1xyz";
-    outcome = PDBSearchPanel.decodeSearchTerm("1xyz", "text");
+    outcome = PDBFTSPanel.decodeSearchTerm("1xyz", "text");
     // System.out.println("2 >>>>>>>>>>> " + outcome);
     assertEquals(expectedString, outcome);
   }
@@ -71,36 +73,42 @@ public class PDBSearchPanelTest
   {
 
     String expectedString = "1xyz:A";
-    String outcome = PDBSearchPanel.getPDBIdwithSpecifiedChain("1xyz",
+    String outcome = PDBFTSPanel.getPDBIdwithSpecifiedChain("1xyz",
             "2xyz;3xyz;1xyz:A");
     System.out.println("1 >>>>>>>>>>> " + outcome);
     assertEquals(expectedString, outcome);
 
     expectedString = "2xyz";
-    outcome = PDBSearchPanel.getPDBIdwithSpecifiedChain("2xyz",
+    outcome = PDBFTSPanel.getPDBIdwithSpecifiedChain("2xyz",
             "1xyz:A;2xyz;3xyz");
     System.out.println("2 >>>>>>>>>>> " + outcome);
     assertEquals(expectedString, outcome);
 
     expectedString = "2xyz:A";
-    outcome = PDBSearchPanel.getPDBIdwithSpecifiedChain("2xyz", "2xyz:A");
+    outcome = PDBFTSPanel.getPDBIdwithSpecifiedChain("2xyz", "2xyz:A");
     System.out.println("3 >>>>>>>>>>> " + outcome);
     assertEquals(expectedString, outcome);
   }
 
-  @Test(groups = { "Network", "External" }, timeOut = 5000)
+  @Test(groups = { "External" }, timeOut = 7000)
   public void txt_search_ActionPerformedTest()
   {
-    PDBSearchPanel searchPanel = new PDBSearchPanel(null);
+    PDBFTSPanel searchPanel = new PDBFTSPanel(null);
     JInternalFrame mainFrame = searchPanel.getMainFrame();
     JTextField txt_search = searchPanel.getTxtSearch();
 
     assertTrue(mainFrame.getTitle().length() == 20);
     assertTrue(mainFrame.getTitle()
             .equalsIgnoreCase("PDB Sequence Fetcher"));
-
     txt_search.setText("ABC");
-
+    try
+    {
+      // wait for web-service to handle response
+      Thread.sleep(3000);
+    } catch (InterruptedException e)
+    {
+      e.printStackTrace();
+    }
     assertTrue(mainFrame.getTitle().length() > 20);
     assertTrue(!mainFrame.getTitle().equalsIgnoreCase(
             "PDB Sequence Fetcher"));
  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
  * The Jalview Authors are detailed in the 'AUTHORS' file.
  */
-package jalview.ws.dbsources;
+package jalview.fts.service.pdb;
 
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.ws.dbsources.PDBRestClient.PDBDocField;
-import jalview.ws.uimodel.PDBRestRequest;
-import jalview.ws.uimodel.PDBRestResponse;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
 
 import java.io.BufferedReader;
 import java.io.FileReader;
@@ -51,7 +51,7 @@ import com.sun.jersey.api.client.WebResource;
 import com.sun.jersey.api.client.config.ClientConfig;
 import com.sun.jersey.api.client.config.DefaultClientConfig;
 
-public class PDBRestClientTest
+public class PDBFTSRestClientTest
 {
 
   @BeforeMethod(alwaysRun = true)
@@ -67,24 +67,37 @@ public class PDBRestClientTest
   @Test(groups = { "External", "Network" })
   public void executeRequestTest()
   {
-    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
-    wantedFields.add(PDBDocField.MOLECULE_TYPE);
-    wantedFields.add(PDBDocField.PDB_ID);
-    wantedFields.add(PDBDocField.GENUS);
-    wantedFields.add(PDBDocField.GENE_NAME);
-    wantedFields.add(PDBDocField.TITLE);
-
-    PDBRestRequest request = new PDBRestRequest();
+    List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("molecule_type"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("pdb_id"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("genus"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("gene_name"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("title"));
+    } catch (Exception e1)
+    {
+      e1.printStackTrace();
+    }
+
+    FTSRestRequest request = new FTSRestRequest();
     request.setAllowEmptySeq(false);
     request.setResponseSize(100);
     request.setFieldToSearchBy("text:");
     request.setSearchTerm("abc");
     request.setWantedFields(wantedFields);
 
-    PDBRestResponse response;
+    FTSRestResponse response;
     try
     {
-      response = new PDBRestClient().executeRequest(request);
+      response = PDBFTSRestClient.getInstance().executeRequest(request);
     } catch (Exception e)
     {
       e.printStackTrace();
@@ -99,16 +112,29 @@ public class PDBRestClientTest
   @Test(groups = { "Functional" })
   public void getPDBDocFieldsAsCommaDelimitedStringTest()
   {
-    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
-    wantedFields.add(PDBDocField.MOLECULE_TYPE);
-    wantedFields.add(PDBDocField.PDB_ID);
-    wantedFields.add(PDBDocField.GENUS);
-    wantedFields.add(PDBDocField.GENE_NAME);
-    wantedFields.add(PDBDocField.TITLE);
+    List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("molecule_type"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("pdb_id"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("genus"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("gene_name"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("title"));
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
 
     String expectedResult = "molecule_type,pdb_id,genus,gene_name,title";
-    String actualResult = PDBRestClient
-            .getPDBDocFieldsAsCommaDelimitedString(wantedFields);
+    String actualResult = PDBFTSRestClient.getInstance()
+            .getDataColumnsFieldsAsCommaDelimitedString(wantedFields);
 
     assertEquals("", expectedResult, actualResult);
   }
@@ -116,14 +142,27 @@ public class PDBRestClientTest
   @Test(groups = { "External, Network" })
   public void parsePDBJsonExceptionStringTest()
   {
-    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
-    wantedFields.add(PDBDocField.MOLECULE_TYPE);
-    wantedFields.add(PDBDocField.PDB_ID);
-    wantedFields.add(PDBDocField.GENUS);
-    wantedFields.add(PDBDocField.GENE_NAME);
-    wantedFields.add(PDBDocField.TITLE);
-
-    PDBRestRequest request = new PDBRestRequest();
+    List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("molecule_type"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("pdb_id"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("genus"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("gene_name"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("title"));
+    } catch (Exception e1)
+    {
+      e1.printStackTrace();
+    }
+
+    FTSRestRequest request = new FTSRestRequest();
     request.setAllowEmptySeq(false);
     request.setResponseSize(100);
     request.setFieldToSearchBy("text:");
@@ -139,7 +178,7 @@ public class PDBRestClientTest
       e.printStackTrace();
     }
 
-    String parsedErrorResponse = PDBRestClient
+    String parsedErrorResponse = PDBFTSRestClient
             .parseJsonExceptionString(jsonErrorResponse);
 
     String expectedErrorMsg = "\n============= PDB Rest Client RunTime error =============\n"
@@ -152,31 +191,46 @@ public class PDBRestClientTest
   }
 
   @Test(
-    groups = { "External", "Network" },
+    groups = { "External" },
     expectedExceptions = Exception.class)
   public void testForExpectedRuntimeException() throws Exception
   {
-    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
-    wantedFields.add(PDBDocField.PDB_ID);
+    List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+    wantedFields.add(PDBFTSRestClient.getInstance()
+            .getDataColumnByNameOrCode("pdb_id"));
 
-    PDBRestRequest request = new PDBRestRequest();
+    FTSRestRequest request = new FTSRestRequest();
     request.setFieldToSearchBy("text:");
     request.setSearchTerm("abc OR text:go:abc");
     request.setWantedFields(wantedFields);
-    new PDBRestClient().executeRequest(request);
+    PDBFTSRestClient.getInstance().executeRequest(request);
   }
 
+    // JBP: Is this actually external ?  Looks like it is mocked
   @Test(groups = { "External" })
   public void parsePDBJsonResponseTest()
   {
-    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
-    wantedFields.add(PDBDocField.MOLECULE_TYPE);
-    wantedFields.add(PDBDocField.PDB_ID);
-    wantedFields.add(PDBDocField.GENUS);
-    wantedFields.add(PDBDocField.GENE_NAME);
-    wantedFields.add(PDBDocField.TITLE);
-
-    PDBRestRequest request = new PDBRestRequest();
+    List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("molecule_type"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("pdb_id"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("genus"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("gene_name"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("title"));
+    } catch (Exception e1)
+    {
+      e1.printStackTrace();
+    }
+
+    FTSRestRequest request = new FTSRestRequest();
     request.setAllowEmptySeq(false);
     request.setWantedFields(wantedFields);
 
@@ -188,7 +242,7 @@ public class PDBRestClientTest
     {
       e.printStackTrace();
     }
-    PDBRestResponse response = PDBRestClient.parsePDBJsonResponse(
+    FTSRestResponse response = PDBFTSRestClient.parsePDBJsonResponse(
             jsonString, request);
     assertTrue(response.getSearchSummary() != null);
     assertTrue(response.getNumberOfItemsFound() == 931);
@@ -198,14 +252,38 @@ public class PDBRestClientTest
   @Test(groups = { "Functional" })
   public void getPDBIdColumIndexTest()
   {
-    List<PDBDocField> wantedFields = new ArrayList<PDBDocField>();
-    wantedFields.add(PDBDocField.MOLECULE_TYPE);
-    wantedFields.add(PDBDocField.GENUS);
-    wantedFields.add(PDBDocField.GENE_NAME);
-    wantedFields.add(PDBDocField.TITLE);
-    wantedFields.add(PDBDocField.PDB_ID);
-    assertEquals(5, PDBRestClient.getPDBIdColumIndex(wantedFields, true));
-    assertEquals(4, PDBRestClient.getPDBIdColumIndex(wantedFields, false));
+    List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("molecule_type"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("genus"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("gene_name"));
+      wantedFields.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("title"));
+      wantedFields
+.add(PDBFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("pdb_id"));
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    try
+    {
+      assertEquals(5,
+ PDBFTSRestClient.getInstance()
+              .getPrimaryKeyColumIndex(wantedFields, true));
+      assertEquals(4,
+ PDBFTSRestClient.getInstance()
+              .getPrimaryKeyColumIndex(wantedFields, false));
+    } catch (Exception e)
+    {
+      // TODO Auto-generated catch block
+      e.printStackTrace();
+    }
   }
 
   @Test(groups = { "External" })
@@ -216,7 +294,7 @@ public class PDBRestClientTest
 
     // Build request parameters for the REST Request
     WebResource webResource = client
-            .resource(PDBRestClient.PDB_SEARCH_ENDPOINT)
+            .resource(PDBFTSRestClient.PDB_SEARCH_ENDPOINT)
             .queryParam("wt", "json").queryParam("rows", String.valueOf(1))
             .queryParam("q", "text:abc AND molecule_sequence:['' TO *]");
 
@@ -255,9 +333,10 @@ public class PDBRestClientTest
 
         JSONObject pdbJsonDoc = docIter.next();
 
-        for (PDBDocField field : PDBDocField.values())
+        for (FTSDataColumnI field : PDBFTSRestClient.getInstance()
+                .getAllFTSDataColumns())
         {
-          if (field == PDBDocField.ALL)
+          if (field.getName().equalsIgnoreCase("ALL"))
           {
             continue;
           }
diff --git a/test/jalview/gui/AlignFrameTest.java b/test/jalview/gui/AlignFrameTest.java
new file mode 100644 (file)
index 0000000..80e3d5a
--- /dev/null
@@ -0,0 +1,71 @@
+package jalview.gui;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+import static org.testng.AssertJUnit.assertTrue;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class AlignFrameTest
+{
+
+  @Test
+  public void testHideFeatureColumns()
+  {
+    SequenceI seq1 = new Sequence("Seq1", "ABCDEFGHIJ");
+    SequenceI seq2 = new Sequence("Seq2", "ABCDEFGHIJ");
+    seq1.addSequenceFeature(new SequenceFeature("Metal", "", 1, 5,
+            Float.NaN, null));
+    seq2.addSequenceFeature(new SequenceFeature("Metal", "", 6, 10,
+            Float.NaN, null));
+    seq1.addSequenceFeature(new SequenceFeature("Turn", "", 2, 4,
+            Float.NaN, null));
+    seq2.addSequenceFeature(new SequenceFeature("Turn", "", 7, 9,
+            Float.NaN, null));
+    AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
+    AlignFrame af = new AlignFrame(al, al.getWidth(), al.getHeight());
+
+    /*
+     * hiding a feature not present does nothing
+     */
+    assertFalse(af.hideFeatureColumns("exon", true));
+    assertTrue(af.getViewport().getColumnSelection().isEmpty());
+    assertTrue(af.getViewport().getColumnSelection().getHiddenColumns()
+            .isEmpty());
+    assertFalse(af.hideFeatureColumns("exon", false));
+    assertTrue(af.getViewport().getColumnSelection().isEmpty());
+    assertTrue(af.getViewport().getColumnSelection().getHiddenColumns()
+            .isEmpty());
+
+    /*
+     * hiding a feature in all columns does nothing
+     */
+    assertFalse(af.hideFeatureColumns("Metal", true));
+    assertTrue(af.getViewport().getColumnSelection().isEmpty());
+    List<int[]> hidden = af.getViewport().getColumnSelection()
+            .getHiddenColumns();
+    assertTrue(hidden.isEmpty());
+
+    /*
+     * hide a feature present in some columns
+     * sequence positions [2-4], [7-9] are column positions
+     * [1-3], [6-8] base zero
+     */
+    assertTrue(af.hideFeatureColumns("Turn", true));
+    hidden = af.getViewport().getColumnSelection()
+            .getHiddenColumns();
+    assertEquals(2, hidden.size());
+    assertEquals(1, hidden.get(0)[0]);
+    assertEquals(3, hidden.get(0)[1]);
+    assertEquals(6, hidden.get(1)[0]);
+    assertEquals(8, hidden.get(1)[1]);
+  }
+}
index 28b9b83..c2209d6 100644 (file)
@@ -94,6 +94,13 @@ public class StructureChooserTest
 
     sc.setStructuresDiscovered(true);
     sc.populateFilterComboBox();
+    try
+    {
+      Thread.sleep(2000);
+    } catch (InterruptedException e)
+    {
+      e.printStackTrace();
+    }
     optionsSize = sc.getCmbFilterOption().getItemCount();
     assertTrue(optionsSize > 3); // if structures are found, filter options
                                  // should be populated
index 6da160d..73d8826 100644 (file)
@@ -26,6 +26,7 @@ import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.api.FeatureRenderer;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceDummy;
@@ -103,7 +104,7 @@ public class FeaturesFileTest
     sf = sfs[4];
     assertEquals("Fer2 Status: True Positive Pfam 8_8%LINK%",
             sf.description);
-    assertEquals("Pfam 8_8|http://pfam.sanger.ac.uk/family/PF00111",
+    assertEquals("Pfam 8_8|http://pfam.xfam.org/family/PF00111",
             sf.links.get(0).toString());
     assertEquals(8, sf.begin);
     assertEquals(83, sf.end);
@@ -275,7 +276,8 @@ public class FeaturesFileTest
     sfs = al.getSequenceAt(2).getDatasetSequence().getSequenceFeatures();
     assertEquals(1, sfs.length);
     sf = sfs[0];
-    assertEquals("uniprot", sf.description);
+    // ID used for description if available
+    assertEquals("$23", sf.description);
     assertEquals(55, sf.begin);
     assertEquals(130, sf.end);
     assertEquals("uniprot", sf.featureGroup);
@@ -411,4 +413,73 @@ public class FeaturesFileTest
             parseResult);
     checkDatasetfromSimpleGff3(dataset);
   }
+
+  @Test(groups = { "Functional" })
+  public void testPrintJalviewFormat() throws Exception
+  {
+    File f = new File("examples/uniref50.fa");
+    AlignmentI al = readAlignmentFile(f);
+    AlignFrame af = new AlignFrame(al, 500, 500);
+    Map<String, Object> colours = af.getFeatureRenderer()
+            .getFeatureColours();
+    String features = "METAL\tcc9900\n"
+            + "GAMMA-TURN\tred|0,255,255|20.0|95.0|below|66.0\n"
+            + "Pfam\tred\n"
+            + "STARTGROUP\tuniprot\n"
+            + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\n"
+            + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\n"
+            + "<html>Pfam domain<a href=\"http://pfam.xfam.org/family/PF00111\">Pfam_3_4</a></html>\tFER_CAPAA\t-1\t20\t20\tPfam\n"
+            + "ENDGROUP\tuniprot\n";
+    FeaturesFile featuresFile = new FeaturesFile(features,
+            FormatAdapter.PASTE);
+    featuresFile.parse(al.getDataset(), colours, false);
+
+    /*
+     * first with no features displayed
+     */
+    FeatureRenderer fr = af.alignPanel.getFeatureRenderer();
+    Map<String, Object> visible = fr
+            .getDisplayedFeatureCols();
+    String exported = featuresFile.printJalviewFormat(
+            al.getSequencesArray(), visible);
+    String expected = "No Features Visible";
+    assertEquals(expected, exported);
+
+    /*
+     * set METAL (in uniprot group) and GAMMA-TURN visible, but not Pfam
+     */
+    fr.setVisible("METAL");
+    fr.setVisible("GAMMA-TURN");
+    visible = fr.getDisplayedFeatureCols();
+    exported = featuresFile.printJalviewFormat(al.getSequencesArray(),
+            visible);
+    expected = "METAL\tcc9900\n"
+            + "GAMMA-TURN\tff0000|00ffff|20.0|95.0|below|66.0\n"
+            + "\nSTARTGROUP\tuniprot\n"
+            + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\t0.0\n"
+            + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
+            + "ENDGROUP\tuniprot\n";
+    assertEquals(expected, exported);
+
+    /*
+     * now set Pfam visible
+     */
+    fr.setVisible("Pfam");
+    visible = fr.getDisplayedFeatureCols();
+    exported = featuresFile.printJalviewFormat(al.getSequencesArray(),
+            visible);
+    /*
+     * note the order of feature types is uncontrolled - derives from
+     * FeaturesDisplayed.featuresDisplayed which is a HashSet
+     */
+    expected = "METAL\tcc9900\n"
+            + "Pfam\tff0000\n"
+            + "GAMMA-TURN\tff0000|00ffff|20.0|95.0|below|66.0\n"
+            + "\nSTARTGROUP\tuniprot\n"
+            + "Iron\tFER_CAPAA\t-1\t39\t39\tMETAL\t0.0\n"
+            + "Turn\tFER_CAPAA\t-1\t36\t38\tGAMMA-TURN\t0.0\n"
+            + "<html>Pfam domain<a href=\"http://pfam.xfam.org/family/PF00111\">Pfam_3_4</a></html>\tFER_CAPAA\t-1\t20\t20\tPfam\t0.0\n"
+            + "ENDGROUP\tuniprot\n";
+    assertEquals(expected, exported);
+  }
 }
index b1efb7a..c00cf06 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.io;
 
+import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
 
@@ -41,24 +42,34 @@ public class IdentifyFileTest
   }
 
   /**
-   * Additional tests for (a) Jalview features file with no colour
-   * specifications (old style 'groups' file) and (b) Jalview features file with
-   * embedded GFF
+   * Additional tests for Jalview features file
    */
   @Test(groups = "Functional")
   public void testIdentify_featureFile()
   {
     IdentifyFile ider = new IdentifyFile();
 
-    // Jalview format with features only, no feature colours
+    /*
+     * Jalview format with features only, no feature colours
+     */
     String data = "Iron-sulfur (2Fe-2S)\tFER_CAPAA\t-1\t39\t39\tMETAL\n"
             + "Iron-phosphorus (2Fe-P)\tID_NOT_SPECIFIED\t2\t86\t87\tMETALLIC\n";
-    Assert.assertEquals(IdentifyFile.FeaturesFile, ider.identify(data, AppletFormatAdapter.PASTE));
+    assertEquals(IdentifyFile.FeaturesFile,
+            ider.identify(data, AppletFormatAdapter.PASTE));
 
-    // Jalview feature colour followed by GFF format feature data
+    /*
+     * Jalview feature colour followed by GFF format feature data
+     */
     data = "METAL\tcc9900\n" + "GFF\n"
             + "FER_CAPAA\tuniprot\tMETAL\t44\t45\t4.0\t.\t.\n";
-    Assert.assertEquals(IdentifyFile.FeaturesFile,
+    assertEquals(IdentifyFile.FeaturesFile,
+            ider.identify(data, AppletFormatAdapter.PASTE));
+
+    /*
+     * Feature with '<' in the name (JAL-2098)
+     */
+    data = "kD < 3\tred\n" + "Low kD\tFER_CAPAA\t-1\t39\t39\tkD < 3\n";
+    assertEquals(IdentifyFile.FeaturesFile,
             ider.identify(data, AppletFormatAdapter.PASTE));
   }
 
index b58a8a6..5e2ceb9 100644 (file)
@@ -422,6 +422,7 @@ public class Jalview2xmlTests
      */
     assertTrue(Jalview2XML.isVersionStringLaterThan(null, null));
     assertTrue(Jalview2XML.isVersionStringLaterThan("2.8.3", null));
+    assertTrue(Jalview2XML.isVersionStringLaterThan(null, "2.8.3"));
     assertTrue(Jalview2XML.isVersionStringLaterThan(null,
             "Development Build"));
     assertTrue(Jalview2XML.isVersionStringLaterThan(null,
diff --git a/test/jalview/io/SequenceAnnotationReportTest.java b/test/jalview/io/SequenceAnnotationReportTest.java
new file mode 100644 (file)
index 0000000..f551571
--- /dev/null
@@ -0,0 +1,166 @@
+package jalview.io;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+import jalview.datamodel.SequenceFeature;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.testng.annotations.Test;
+
+public class SequenceAnnotationReportTest
+{
+  @Test(groups = "Functional")
+  public void testAppendFeature_disulfideBond()
+  {
+    SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
+    StringBuffer sb = new StringBuffer();
+    sb.append("123456");
+    SequenceFeature sf = new SequenceFeature("disulfide bond", "desc", 1,
+            3, 1.2f, "group");
+
+    // residuePos == 2 does not match start or end of feature, nothing done:
+    sar.appendFeature(sb, 2, null, sf);
+    assertEquals("123456", sb.toString());
+
+    // residuePos == 1 matches start of feature, text appended (but no <br>)
+    // feature score is not included
+    sar.appendFeature(sb, 1, null, sf);
+    assertEquals("123456disulfide bond 1:3", sb.toString());
+
+    // residuePos == 3 matches end of feature, text appended
+    // <br> is prefixed once sb.length() > 6
+    sar.appendFeature(sb, 3, null, sf);
+    assertEquals("123456disulfide bond 1:3<br>disulfide bond 1:3",
+            sb.toString());
+  }
+
+  @Test(groups = "Functional")
+  public void testAppendFeature_status()
+  {
+    SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
+    StringBuffer sb = new StringBuffer();
+    SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3,
+            Float.NaN, "group");
+    sf.setStatus("Confirmed");
+  
+    sar.appendFeature(sb, 1, null, sf);
+    assertEquals("METAL 1 3; Fe2-S; (Confirmed)", sb.toString());
+  }
+
+  @Test(groups = "Functional")
+  public void testAppendFeature_withScore()
+  {
+    SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
+    StringBuffer sb = new StringBuffer();
+    SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3, 1.3f,
+            "group");
+
+    Map<String, float[][]> minmax = new Hashtable<String, float[][]>();
+    sar.appendFeature(sb, 1, minmax, sf);
+    /*
+     * map has no entry for this feature type - score is not shown:
+     */
+    assertEquals("METAL 1 3; Fe2-S", sb.toString());
+
+    /*
+     * map has entry for this feature type - score is shown:
+     */
+    minmax.put("METAL", new float[][] { { 0f, 1f }, null });
+    sar.appendFeature(sb, 1, minmax, sf);
+    // <br> is appended to a buffer > 6 in length
+    assertEquals("METAL 1 3; Fe2-S<br>METAL 1 3; Fe2-S Score=1.3",
+            sb.toString());
+
+    /*
+     * map has min == max for this feature type - score is not shown:
+     */
+    minmax.put("METAL", new float[][] { { 2f, 2f }, null });
+    sb.setLength(0);
+    sar.appendFeature(sb, 1, minmax, sf);
+    assertEquals("METAL 1 3; Fe2-S", sb.toString());
+  }
+
+  @Test(groups = "Functional")
+  public void testAppendFeature_noScore()
+  {
+    SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
+    StringBuffer sb = new StringBuffer();
+    SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3,
+            Float.NaN, "group");
+  
+    sar.appendFeature(sb, 1, null, sf);
+    assertEquals("METAL 1 3; Fe2-S", sb.toString());
+  }
+
+  @Test(groups = "Functional")
+  public void testAppendFeature_clinicalSignificance()
+  {
+    SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
+    StringBuffer sb = new StringBuffer();
+    SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3,
+            Float.NaN, "group");
+    sf.setValue("clinical_significance", "Benign");
+  
+    sar.appendFeature(sb, 1, null, sf);
+    assertEquals("METAL 1 3; Fe2-S; Benign", sb.toString());
+  }
+
+  @Test(groups = "Functional")
+  public void testAppendFeature_withScoreStatusClinicalSignificance()
+  {
+    SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
+    StringBuffer sb = new StringBuffer();
+    SequenceFeature sf = new SequenceFeature("METAL", "Fe2-S", 1, 3, 1.3f,
+            "group");
+    sf.setStatus("Confirmed");
+    sf.setValue("clinical_significance", "Benign");
+    Map<String, float[][]> minmax = new Hashtable<String, float[][]>();
+    minmax.put("METAL", new float[][] { { 0f, 1f }, null });
+    sar.appendFeature(sb, 1, minmax, sf);
+
+    assertEquals("METAL 1 3; Fe2-S Score=1.3; (Confirmed); Benign",
+            sb.toString());
+  }
+
+  @Test(groups = "Functional")
+  public void testAppendFeature_DescEqualsType()
+  {
+    SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
+    StringBuffer sb = new StringBuffer();
+    SequenceFeature sf = new SequenceFeature("METAL", "METAL", 1, 3,
+            Float.NaN, "group");
+  
+    // description is not included if it duplicates type:
+    sar.appendFeature(sb, 1, null, sf);
+    assertEquals("METAL 1 3", sb.toString());
+
+    sb.setLength(0);
+    sf.setDescription("Metal");
+    // test is case-sensitive:
+    sar.appendFeature(sb, 1, null, sf);
+    assertEquals("METAL 1 3; Metal", sb.toString());
+  }
+
+  @Test(groups = "Functional")
+  public void testAppendFeature_stripHtml()
+  {
+    SequenceAnnotationReport sar = new SequenceAnnotationReport(null);
+    StringBuffer sb = new StringBuffer();
+    SequenceFeature sf = new SequenceFeature("METAL",
+            "<html><body>hello<em>world</em></body></html>", 1, 3,
+            Float.NaN, "group");
+  
+    sar.appendFeature(sb, 1, null, sf);
+    // !! strips off </body> but not <body> ??
+    assertEquals("METAL 1 3; <body>hello<em>world</em>",
+            sb.toString());
+
+    sb.setLength(0);
+    sf.setDescription("<br>&kHD>6");
+    sar.appendFeature(sb, 1, null, sf);
+    // if no <html> tag, html-encodes > and < (only):
+    assertEquals("METAL 1 3; &lt;br&gt;&kHD&gt;6", sb.toString());
+  }
+}
index 1630110..8674ed8 100644 (file)
@@ -250,6 +250,7 @@ public class Mapping
   @Test(groups = { "Functional" })
   public void compareTransferredToRefPDBAnnot() throws Exception
   {
+    StructureViewSettings.setShowSeqFeatures(true);
     AlignFrame ref = new FileLoader(false)
             .LoadFileWaitTillLoaded("test/jalview/ext/jmol/1QCF.pdb",
                     jalview.io.FormatAdapter.FILE);
index 3529375..999d158 100644 (file)
@@ -43,6 +43,7 @@ public class StructureSelectionManagerTest
   @BeforeMethod(alwaysRun = true)
   public void setUp()
   {
+    StructureViewSettings.setShowSeqFeatures(true);
     ssm = new StructureSelectionManager();
   }
 
index 2b12a72..a82b9c0 100644 (file)
@@ -72,4 +72,65 @@ public class ColorUtilsTest
     assertEquals("#800080", ColorUtils.toTkCode(new Color(128, 0, 128))); // purple
     assertEquals("#00ff00", ColorUtils.toTkCode(new Color(0, 255, 0))); // lime
   }
+
+  @Test(groups = { "Functional" })
+  public void testGetGraduatedColour()
+  {
+    Color minColour = new Color(100, 100, 100);
+    Color maxColour = new Color(180, 200, 220);
+
+    /*
+     * value half-way between min and max
+     */
+    Color col = ColorUtils.getGraduatedColour(20f, 10f, minColour, 30f,
+            maxColour);
+    assertEquals(140, col.getRed());
+    assertEquals(150, col.getGreen());
+    assertEquals(160, col.getBlue());
+
+    /*
+     * value two-thirds of the way between min and max
+     */
+    col = ColorUtils
+            .getGraduatedColour(30f, 10f, minColour, 40f, maxColour);
+    assertEquals(153, col.getRed());
+    // Color constructor rounds float value to nearest int
+    assertEquals(167, col.getGreen());
+    assertEquals(180, col.getBlue());
+
+    /*
+     * value = min
+     */
+    col = ColorUtils
+            .getGraduatedColour(10f, 10f, minColour, 30f, maxColour);
+    assertEquals(minColour, col);
+
+    /*
+     * value = max
+     */
+    col = ColorUtils
+            .getGraduatedColour(30f, 10f, minColour, 30f, maxColour);
+    assertEquals(maxColour, col);
+
+    /*
+     * value < min
+     */
+    col = ColorUtils.getGraduatedColour(0f, 10f, minColour, 30f, maxColour);
+    assertEquals(minColour, col);
+
+    /*
+     * value > max
+     */
+    col = ColorUtils
+            .getGraduatedColour(40f, 10f, minColour, 30f,
+            maxColour);
+    assertEquals(maxColour, col);
+
+    /*
+     * min = max
+     */
+    col = ColorUtils
+            .getGraduatedColour(40f, 10f, minColour, 10f, maxColour);
+    assertEquals(minColour, col);
+  }
 }
index 0c2c998..9aab66c 100644 (file)
@@ -49,7 +49,7 @@ public class ComparisonTest
    * AGCTU. Test is not case-sensitive and ignores gaps.
    */
   @Test(groups = { "Functional" })
-  public void testIsNucleotide()
+  public void testIsNucleotide_sequences()
   {
     SequenceI seq = new Sequence("eightypercent", "agctuAGCPV");
     assertFalse(Comparison.isNucleotide(new SequenceI[] { seq }));
@@ -130,6 +130,23 @@ public class ComparisonTest
             0.001f);
   }
 
+  @Test(groups = { "Functional" })
+  public void testIsNucleotide()
+  {
+    assertTrue(Comparison.isNucleotide('a'));
+    assertTrue(Comparison.isNucleotide('A'));
+    assertTrue(Comparison.isNucleotide('c'));
+    assertTrue(Comparison.isNucleotide('C'));
+    assertTrue(Comparison.isNucleotide('g'));
+    assertTrue(Comparison.isNucleotide('G'));
+    assertTrue(Comparison.isNucleotide('t'));
+    assertTrue(Comparison.isNucleotide('T'));
+    assertTrue(Comparison.isNucleotide('u'));
+    assertTrue(Comparison.isNucleotide('U'));
+    assertFalse(Comparison.isNucleotide('-'));
+    assertFalse(Comparison.isNucleotide('P'));
+  }
+
   /**
    * Test the percentage identity calculation for two sequences
    */
@@ -158,4 +175,17 @@ public class ComparisonTest
     assertEquals(87.5f, Comparison.PID(seq1, seq2, 0, length, false, true),
             0.001f);
   }
+
+  @Test(groups = { "Functional" })
+  public void testIsNucleotideSequence()
+  {
+    assertFalse(Comparison.isNucleotideSequence(null, true));
+    assertTrue(Comparison.isNucleotideSequence("", true));
+    assertTrue(Comparison.isNucleotideSequence("aAgGcCtTuU", true));
+    assertTrue(Comparison.isNucleotideSequence("aAgGcCtTuU", false));
+    assertFalse(Comparison.isNucleotideSequence("xAgGcCtTuU", false));
+    assertFalse(Comparison.isNucleotideSequence("aAgGcCtTuUx", false));
+    assertTrue(Comparison.isNucleotideSequence("a A-g.GcCtTuU", true));
+    assertFalse(Comparison.isNucleotideSequence("a A-g.GcCtTuU", false));
+  }
 }
diff --git a/test/jalview/util/DnaUtilsTest.java b/test/jalview/util/DnaUtilsTest.java
new file mode 100644 (file)
index 0000000..6623c13
--- /dev/null
@@ -0,0 +1,142 @@
+package jalview.util;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.fail;
+
+import java.util.List;
+
+import org.testng.annotations.Test;
+
+public class DnaUtilsTest
+{
+  /**
+   * Tests for parsing an ENA/GenBank location specifier
+   * 
+   * @see http://www.insdc.org/files/feature_table.html#3.4
+   */
+  @Test(groups = { "Functional" })
+  public void testParseLocation()
+  {
+    /*
+     * single locus
+     */
+    List<int[]> ranges = DnaUtils.parseLocation("467");
+    assertEquals(1, ranges.size());
+    assertEquals(467, ranges.get(0)[0]);
+    assertEquals(467, ranges.get(0)[1]);
+
+    /*
+     * simple range
+     */
+    ranges = DnaUtils.parseLocation("12..78");
+    assertEquals(1, ranges.size());
+    assertEquals(12, ranges.get(0)[0]);
+    assertEquals(78, ranges.get(0)[1]);
+
+    /*
+     * join of simple ranges
+     */
+    ranges = DnaUtils.parseLocation("join(12..78,134..202,322..345)");
+    assertEquals(3, ranges.size());
+    assertEquals(12, ranges.get(0)[0]);
+    assertEquals(78, ranges.get(0)[1]);
+    assertEquals(134, ranges.get(1)[0]);
+    assertEquals(202, ranges.get(1)[1]);
+    assertEquals(322, ranges.get(2)[0]);
+    assertEquals(345, ranges.get(2)[1]);
+
+    /*
+     * complement of a simple range
+     */
+    ranges = DnaUtils.parseLocation("complement(34..126)");
+    assertEquals(1, ranges.size());
+    assertEquals(126, ranges.get(0)[0]);
+    assertEquals(34, ranges.get(0)[1]);
+
+    /*
+     * complement of a join
+     */
+    ranges = DnaUtils
+            .parseLocation("complement(join(2691..4571,4918..5163))");
+    assertEquals(2, ranges.size());
+    assertEquals(5163, ranges.get(0)[0]);
+    assertEquals(4918, ranges.get(0)[1]);
+    assertEquals(4571, ranges.get(1)[0]);
+    assertEquals(2691, ranges.get(1)[1]);
+
+    /*
+     * join of two complements
+     */
+    ranges = DnaUtils
+            .parseLocation("join(complement(4918..5163),complement(2691..4571))");
+    assertEquals(2, ranges.size());
+    assertEquals(5163, ranges.get(0)[0]);
+    assertEquals(4918, ranges.get(0)[1]);
+    assertEquals(4571, ranges.get(1)[0]);
+    assertEquals(2691, ranges.get(1)[1]);
+
+    /*
+     * join complement to non-complement
+     * @see http://www.ncbi.nlm.nih.gov/genbank/genomesubmit_annotation/ Transpliced Genes
+     */
+    ranges = DnaUtils
+            .parseLocation("join(complement(36618..36700),86988..87064)");
+    assertEquals(2, ranges.size());
+    assertEquals(36700, ranges.get(0)[0]);
+    assertEquals(36618, ranges.get(0)[1]);
+    assertEquals(86988, ranges.get(1)[0]);
+    assertEquals(87064, ranges.get(1)[1]);
+
+    /*
+     * beyond 5' or 3' locus
+     */
+    ranges = DnaUtils.parseLocation("<34..126");
+    assertEquals(1, ranges.size());
+    assertEquals(34, ranges.get(0)[0]);
+    assertEquals(126, ranges.get(0)[1]);
+    ranges = DnaUtils.parseLocation("35..>127");
+    assertEquals(1, ranges.size());
+    assertEquals(35, ranges.get(0)[0]);
+    assertEquals(127, ranges.get(0)[1]);
+
+    /*
+     * valid things we don't yet handle
+     */
+    assertNull(DnaUtils.parseLocation("34.126"));
+    assertNull(DnaUtils.parseLocation("34^126"));
+    assertNull(DnaUtils.parseLocation("order(34..126,130..180)"));
+
+    /*
+     * invalid things
+     */
+    assertNull(DnaUtils.parseLocation(""));
+    assertNull(DnaUtils.parseLocation("JOIN(1..2)"));
+    assertNull(DnaUtils.parseLocation("join(1..2"));
+    assertNull(DnaUtils.parseLocation("join(1..2("));
+    assertNull(DnaUtils.parseLocation("complement(1..2"));
+    assertNull(DnaUtils.parseLocation("complement(1..2("));
+    try
+    {
+      assertNull(DnaUtils.parseLocation(null));
+      fail("Expected exception");
+    } catch (NullPointerException e)
+    {
+      // expected
+    }
+
+    /*
+     * nested joins are not allowed; just as well since this fails to parse
+     * (splitting tokens by comma fragments the inner join expression)
+     */
+    assertNull(DnaUtils
+            .parseLocation("join(1..2,join(4..5,10..12),18..22)"));
+    /*
+     * complement may not enclose multiple ranges 
+     * parsing fails for the same reason
+     */
+    assertNull(DnaUtils
+            .parseLocation("join(complement(36618..36700,4000..4200),86988..87064)"));
+  }
+
+}
index dc2555b..4dc44d4 100644 (file)
@@ -165,4 +165,57 @@ public class StringUtilsTest
     assertEquals(0,
             StringUtils.parseInt(String.valueOf(Integer.MAX_VALUE) + "1"));
   }
+
+  @Test(groups = { "Functional" })
+  public void testCompareVersions()
+  {
+    assertEquals(0, StringUtils.compareVersions(null, null));
+    assertEquals(0, StringUtils.compareVersions("2.8.3", null));
+
+    /*
+     * same version returns 0
+     */
+    assertEquals(0, StringUtils.compareVersions("2.8", "2.8"));
+    assertEquals(0, StringUtils.compareVersions("2.8.3", "2.8.3"));
+    assertEquals(0, StringUtils.compareVersions("2.8.3b1", "2.8.3b1", "b"));
+    assertEquals(0, StringUtils.compareVersions("2.8.3B1", "2.8.3b1", "b"));
+    assertEquals(0, StringUtils.compareVersions("2.8.3b1", "2.8.3B1", "b"));
+
+    /*
+     * v1 < v2 returns -1
+     */
+    assertEquals(-1, StringUtils.compareVersions("2.8.3", "2.8.4"));
+    assertEquals(-1, StringUtils.compareVersions("2.8.3", "2.9"));
+    assertEquals(-1, StringUtils.compareVersions("2.8.3", "2.9.2"));
+    assertEquals(-1, StringUtils.compareVersions("2.8", "2.8.3"));
+    assertEquals(-1, StringUtils.compareVersions("2.8.3", "2.8.3b1", "b"));
+    assertEquals(-1, StringUtils.compareVersions("2.8.3b1", "2.8.3b2", "b"));
+    assertEquals(-1, StringUtils.compareVersions("2.8", "2.8.0", "b"));
+    assertEquals(-1, StringUtils.compareVersions("2", "12"));
+    assertEquals(-1, StringUtils.compareVersions("3.2.4", "3.12.11"));
+
+    /*
+     * v1 > v2 returns +1
+     */
+    assertEquals(1, StringUtils.compareVersions("2.8.3", "2.8"));
+    assertEquals(1, StringUtils.compareVersions("2.8.0", "2.8"));
+    assertEquals(1, StringUtils.compareVersions("2.8.4", "2.8.3"));
+    assertEquals(1, StringUtils.compareVersions("2.8.3b1", "2.8.3", "b"));
+    assertEquals(1, StringUtils.compareVersions("2.8.3", "2.8.2b1", "b"));
+    assertEquals(1, StringUtils.compareVersions("2.8.0b2", "2.8.0b1", "b"));
+    assertEquals(1, StringUtils.compareVersions("12", "2"));
+    assertEquals(1, StringUtils.compareVersions("3.12.11", "3.2.4"));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testToSentenceCase()
+  {
+    assertEquals("John", StringUtils.toSentenceCase("john"));
+    assertEquals("John", StringUtils.toSentenceCase("JOHN"));
+    assertEquals("John and james",
+            StringUtils.toSentenceCase("JOHN and JAMES"));
+    assertEquals("J", StringUtils.toSentenceCase("j"));
+    assertEquals("", StringUtils.toSentenceCase(""));
+    assertNull(StringUtils.toSentenceCase(null));
+  }
 }
diff --git a/test/jalview/ws/dbsources/XfamFetcherTest.java b/test/jalview/ws/dbsources/XfamFetcherTest.java
new file mode 100644 (file)
index 0000000..c894fd1
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+package jalview.ws.dbsources;
+
+import jalview.datamodel.AlignmentI;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class XfamFetcherTest
+{
+  @Test(groups = { "External" })
+  public void testRfamSeed() throws Exception
+  {
+    // RfamFull rff = new RfamFull();
+    RfamSeed rfs = new RfamSeed();
+
+    AlignmentI seedrf = rfs.getSequenceRecords(rfs.getTestQuery());
+    Assert.assertNotNull(seedrf, "Seed Alignment for " + rfs.getTestQuery()
+            + " didn't retrieve.");
+    Assert.assertTrue(seedrf.getHeight() > 1,
+            "Seed Alignment for " + rfs.getTestQuery()
+                    + " didn't contain more than one sequence.");
+  }
+
+  @Test(groups = { "External" })
+  public void testPfamFullAndSeed() throws Exception
+  {
+    PfamFull pff = new PfamFull();
+    PfamSeed pfseed = new PfamSeed();
+
+    AlignmentI fullpf = pff.getSequenceRecords(pff.getTestQuery());
+    Assert.assertNotNull(fullpf, "Full Alignment for " + pff.getTestQuery()
+            + " didn't retrieve.");
+    Assert.assertTrue(fullpf.getHeight() > 1,
+            "Full Alignment for " + pff.getTestQuery()
+                    + " didn't have more than one sequence.");
+    AlignmentI seedpf = pfseed.getSequenceRecords(pff.getTestQuery());
+    Assert.assertNotNull(seedpf, "Seed Alignment for " + pff.getTestQuery()
+            + " didn't retrieve.");
+
+    Assert.assertTrue(seedpf.getHeight() < fullpf.getHeight(),
+            "Expected Full alignment to have more sequences than seed for "
+                    + pff.getTestQuery());
+  }
+}
index 64840c2..557ef7e 100644 (file)
@@ -40,7 +40,7 @@ import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-@Test(groups = { "Network" })
+@Test(groups = { "External" })
 public class DisorderAnnotExportImport
 {
   public static String testseqs = "examples/uniref50.fa";
index 83d1a98..fc799bb 100755 (executable)
@@ -2025,7 +2025,7 @@ and any path to a file to save to the file]]></string>
                                                                <string><![CDATA[664]]></string>
                                                        </property>
                                                        <property name="sourceName">
-                                                               <string><![CDATA[groovy-all-1.8.2.jar]]></string>
+                                                               <string><![CDATA[groovy-all-2.4.6-indy.jar]]></string>
                                                        </property>
                                                        <property name="overrideUnixPermissions">
                                                                <boolean>false</boolean>
@@ -2043,7 +2043,7 @@ and any path to a file to save to the file]]></string>
                                                                <boolean>true</boolean>
                                                        </property>
                                                        <property name="destinationName">
-                                                               <string><![CDATA[groovy-all-1.8.2.jar]]></string>
+                                                               <string><![CDATA[groovy-all-2.4.6-indy.jar]]></string>
                                                        </property>
                                                        <property name="fileSize">
                                                                <long>6149494</long>
@@ -2891,6 +2891,58 @@ and any path to a file to save to the file]]></string>
                                                        </property>
                                                </object>
                                        </method>
+                                       <method name="addElement">
+                                               <object class="com.zerog.ia.installer.actions.InstallZipfile" objectID="1000ddddfab939">
+                                                       <property name="belongsToUninstallPhase">
+                                                               <boolean>false</boolean>
+                                                       </property>
+                                                       <property name="rollbackEnabledCancel">
+                                                               <boolean>true</boolean>
+                                                       </property>
+                                                       <property name="rollbackEnabledError">
+                                                               <boolean>true</boolean>
+                                                       </property>
+                                                       <property name="ruleExpression">
+                                                               <string><![CDATA[]]></string>
+                                                       </property>
+                                                       <property name="unixPermissions">
+                                                               <string><![CDATA[664]]></string>
+                                                       </property>
+                                                       <property name="sourceName">
+                                                               <string><![CDATA[servlet-api-3.1.jar]]></string>
+                                                       </property>
+                                                       <property name="overrideUnixPermissions">
+                                                               <boolean>false</boolean>
+                                                       </property>
+                                                       <property name="sourcePath">
+                                                               <string><![CDATA[/home/cruisecontrol/jalview/lib/]]></string>
+                                                       </property>
+                                                       <property name="shouldUninstall">
+                                                               <boolean>true</boolean>
+                                                       </property>
+                                                       <property name="rollbackEnabledCancel">
+                                                               <boolean>true</boolean>
+                                                       </property>
+                                                       <property name="rollbackEnabledError">
+                                                               <boolean>true</boolean>
+                                                       </property>
+                                                       <property name="destinationName">
+                                                               <string><![CDATA[servlet-api-3.1.jar]]></string>
+                                                       </property>
+                                                       <property name="fileSize">
+                                                               <long>16046</long>
+                                                       </property>
+                                                       <property name="macBinary">
+                                                               <boolean>false</boolean>
+                                                       </property>
+                                                       <property name="targetCheckKind">
+                                                               <int>0</int>
+                                                       </property>
+                                                       <property name="ruleExpression">
+                                                               <string><![CDATA[]]></string>
+                                                       </property>
+                                               </object>
+                                       </method>
                                </object>
                        </property>
                        <property name="rulesFailedMessage">
@@ -7360,6 +7412,7 @@ and any path to a file to read from that file]]></string>
                                                                                <object refID="1f46efeefab93"/>
                                                                                <object refID="1936efeefab93"/>
                                                                                <object refID="1000ddddfab93"/>
+                                                                               <object refID="1000ddddfab939"/>
                                                                                <object refID="10936efeefab93"/>
                                                                                <object refID="11936efeefab93"/>
                                                                                <object refID="12936efeefab93"/>
@@ -7948,6 +8001,7 @@ and any path to a file to read from that file]]></string>
                                                                                                <object refID="1f46efeefab93"/>
                                                                                                <object refID="1936efeefab93"/>
                                                                                                <object refID="1000ddddfab93"/>
+                                                                                               <object refID="1000ddddfab939"/>
                                                                                                <object refID="10936efeefab93"/>
                                                                                                <object refID="11936efeefab93"/>
                                                                                                <object refID="12936efeefab93"/>