From: gmungoc Date: Mon, 19 Sep 2016 16:56:18 +0000 (+0100) Subject: Merge branch 'develop' into features/JAL-2094_colourInterface X-Git-Url: http://source.jalview.org/gitweb/?p=jalview.git;a=commitdiff_plain;h=refs%2Fheads%2Ffeatures%2FJAL-2094_colourInterface;hp=d737688dfdb8d00ed63a699fb86548499d75bcf2 Merge branch 'develop' into features/JAL-2094_colourInterface Conflicts: src/jalview/api/FeatureColourI.java src/jalview/api/FeatureRenderer.java src/jalview/appletgui/FeatureColourChooser.java src/jalview/appletgui/FeatureRenderer.java src/jalview/appletgui/FeatureSettings.java src/jalview/appletgui/SeqCanvas.java src/jalview/appletgui/SequenceRenderer.java src/jalview/appletgui/UserDefinedColours.java src/jalview/ext/ensembl/EnsemblGene.java src/jalview/gui/FeatureColourChooser.java src/jalview/gui/FeatureRenderer.java src/jalview/gui/FeatureSettings.java src/jalview/gui/Jalview2XML.java src/jalview/gui/SeqCanvas.java src/jalview/gui/TreeCanvas.java src/jalview/io/FeaturesFile.java src/jalview/io/PDBFeatureSettings.java src/jalview/renderer/seqfeatures/FeatureRenderer.java src/jalview/schemes/FeatureColour.java src/jalview/schemes/FeatureColourAdapter.java src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java src/jalview/ws/jws1/SeqSearchWSThread.java src/jalview/ws/jws2/AADisorderClient.java test/jalview/io/FeaturesFileTest.java test/jalview/schemes/FeatureColourTest.java --- diff --git a/.checkstyle b/.checkstyle new file mode 100644 index 0000000..0329bb7 --- /dev/null +++ b/.checkstyle @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/build.xml b/build.xml index 57cd9d2..184596c 100755 --- a/build.xml +++ b/build.xml @@ -27,7 +27,9 @@ - + + + @@ -88,6 +90,9 @@ + + + @@ -374,7 +379,7 @@ - @@ -382,7 +387,7 @@ - + @@ -516,7 +521,7 @@ - + @@ -611,16 +616,16 @@ - - + + - - - + + + @@ -637,7 +642,7 @@ - + @@ -649,14 +654,14 @@ - + - - + + - + -

Notes on applet deployment

- - -

**NEW FEATURES** in Jalview 2.9

+

Notes on applet deployment

+ + + + + + + + + + + + + + + + + + + + + + +
Required Dependency Downloads
DependencyDescription
JalviewApplet.jar Main Jalview Applet Jar
JmolApplet-14.2.14_2015.06.11.jar Jmol Applet Jar
java-json.jarRequired for BioJSON Generation
json_simple-1.1.jarRequired for BioJSON Generation
+ +

To run Jalview applet in your web page download the Jars listed above. The snippet below shows a minimal code for embedding Jalview applet into a web page. +


+<applet code="jalview.bin.JalviewLite" width="756" height="560" archive="jalviewApplet.jar,JmolApplet-14.2.14_2015.06.11.jar,java-json.jar,json_simple-1.1.jar">
+	<param name="permissions" value="sandbox" />
+	<param name="file" value="plantfdx.fa" />
+	<param name="features" value="plantfdx.features" />
+	<param name="userDefinedColour" value="C=yellow; R,K,H=FF5555; D,E=5555FF" />
+	<param name="showFullId" value="false" />
+	<param name="embedded" value="true" />
+	<param name="linkLabel_1" value="Uniprot" />
+	<param name="linkUrl_1"	value="http://www.uniprot.org/uniprot/$SEQUENCE_ID$" />
+	<param name="linkLabel_2" value="EMBL-EBI Search" />
+	<param name="linkUrl_2"	value="http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$SEQUENCE_ID$" />
+	<param name="APPLICATION_URL" value="http://www.jalview.org/services/launchApp" />
+</applet>
+
+ + + + +
    +
  • View full list of supported applet parameters here.
  • +
  • Package all your data files into a single (or multiple) zip / + jar files. This is very useful to reduce download time of large data + files. The applet archive tag can take multiple entries separated by + commas, eg
    <applet code="jalview.bin.JalviewLite" archive="jalviewApplet.jar, mydata.zip">
    +            
    +
  • +
  • Use Jalview for input to a HTML form. For an example of how to + code this using Javascript, click here.
    +
  • +
  • Embed Jalview into the web page, without the "Start + Jalview" button by setting the embed parameter to true;
    + <param name="embedded" value="true"> +
  • +
  • For more examples, see the links to the left.
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Applet Release History
ReleaseNew Features / required changes
2.9
(Latest)
  • Split Views for cDNA and Protein alignments
    Specify second alignment with 'file2' parameter, and set cDNA/Protein column scaling with scaleProteinAsCdna
  • @@ -54,9 +107,11 @@
    archive="jalviewApplet.jar,JmolApplet-14.2.14_2015.06.11.jar,java-json.jar,json_simple-1.1.jar"
-

-

**NEW FEATURES** in Jalview 2.8

-
    +
2.8
  • Normalised sequence logo display
  • RNA secondary structure annotation row @@ -71,9 +126,11 @@ original Jalview structure viewer will still be available.
  • -
-

**NEW FEATURES** in Jalview 2.7

-
    +
2.7
  • Javascript callbacks capabilities
    • oninit parameter and methods for registering javascript handlers for selections, mouseovers and linking to Jmol applets on the page.
    • To use javascript callbacks, ensure the applet tag includes the 'mayscript' attribute - either as a parameter (<param name="mayscript" value="true"/;gt;) or as a bare attribute in the applet html tag).
  • @@ -86,13 +143,17 @@ the Jmol binary distribution available at the Jmol Sourceforge site, or download the Jmol applet from here
  • Minimum recommended version of Java runtime for the applet is now 1.5 (JalviewLite v2.6 without the Jmol viewer may work ok on earlier Java environments but compatibility can no-longer be guaranteed).
  • -
-
**NEW FEATURES** in Jalview 2.5

-
    -
  • New parameters to control display of tree annotation, width of alignment columns, and to disable the jalview button and check for Jmol on startup.
  • -
-
**NEW FEATURES** in Jalview 2.4

-
    +
2.5
    +
  • New parameters to control display of tree annotation, width of alignment columns, and to disable the jalview button and check for Jmol on startup.
+
2.4
  • New applet API methods for feature display control, views, and obtaining current selection via javascript.
  • Group show and hide parameters: "showfeaturegroups" and @@ -104,9 +165,11 @@
  • "debug" parameter to control verbosity of the applet's console output.
  • "showbutton" parameter to disable launch button and open JalviewLite immediatly.
  • "nojmol" parameter to disable check for Jmol classes.
  • -

- **NEW FEATURES** in Jalview 2.3

-
    +
2.3
  • Note that Parameter "PDBFile" now takes the PDB file followed by a space separated list of alignment sequence ids to associate the structure to. It is also possible to associate @@ -126,11 +189,12 @@
  • Note parameter "PDBSeq" is no longer required.
  • Jalview 2.3 was updated to work with Jmol 11. See the versions archive if you want to download the old Jmol applet.
  • -

     

    - -
- **NEW FEATURES** in Jalview 2.1 -
    + +
2.1
  • Jalview Applet can read and display JNet secondary structure annotation directly via the jnetfile parameter.
  • @@ -158,5 +222,9 @@ <param name="sequence2" value="FER1_PEA/14-29 TSFLRTQPMPMSVTTT">
    (All the usual Jalview File formats are valid, however each new line in an alignment file must be entered as a parameter) -
+
+ + diff --git a/examples/appletParameters.html b/examples/appletParameters.html index 433f5a9..cc95ecb 100644 --- a/examples/appletParameters.html +++ b/examples/appletParameters.html @@ -28,7 +28,7 @@ which are described below. Once initialised, the applet can be interacted with via its Javascript API.

Issues arising from tightening of Java Security default settings
JalviewLite is provided as a signed applet with 'sandbox' permissions and wildcards that allow it to be run from any website. Unfortunately, earlier versions of Java may not be compatible with these settings.

-

For additional deployment notes, see below.

+

For additional deployment notes, see Applet Deployment.

Applet Parameters


The applet takes the following initialisation parameters.

diff --git a/examples/applets.html b/examples/applets.html index 34118b8..1f65565 100644 --- a/examples/applets.html +++ b/examples/applets.html @@ -22,19 +22,14 @@
-
-
-

Quick Links to jars for example:
jalviewApplet.jar and JmolApplet.jar -

-
-

JalviewLite Button Examples

-Try out JalviewLite by pressing one of the buttons below.
- For more information on how to use the applet in your website, see the applet parameters and other documentation in the links to the left.

+Try out JalviewLite by pressing one of the buttons below. + View the source for the examples below here (If the link doesn't work on your browser try going to this page and viewing the page source manually).
+ For more information on how to use the applet in your website, see the applet deployment,applet parameters, and other documentation in the links to the left.

 

Ferredoxins, chloroplast precursor related UniRef50 diff --git a/examples/embedded.html b/examples/embedded.html index aa67325..0d5ddf3 100644 --- a/examples/embedded.html +++ b/examples/embedded.html @@ -23,9 +23,6 @@
-
-

Quick Links to jars for example:
jalviewApplet.jar and JmolApplet.jar -

@@ -40,6 +37,7 @@
  • plantfdx.annotations - Jalview Alignment Annotations File
  • + View the source code for this example here (If the link doesn't work on your browser try going to this page and viewing the page source manually).

    diff --git a/examples/embeddedWJmol.html b/examples/embeddedWJmol.html index f629312..1ecff58 100644 --- a/examples/embeddedWJmol.html +++ b/examples/embeddedWJmol.html @@ -260,11 +260,7 @@ jmolInitialize("","JmolApplet-14.2.14_2015.06.11.jar"); modeltofiles+="1gaq.txt";
    -
    -
    -

    Quick Links to jars for example:
    jalviewApplet.jar and JmolApplet.jar -

    -
    +
    diff --git a/examples/exampleFeatures.txt b/examples/exampleFeatures.txt index b7038f5..7c8d26c 100755 --- a/examples/exampleFeatures.txt +++ b/examples/exampleFeatures.txt @@ -28,73 +28,74 @@ ST-MOTIF label|||0|0 #ac25a1 STARTGROUP uniprot +Pfam family 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 -Fer2 Status: True Positive Pfam 8_8 FER_CAPAA -1 8 83 Pfam +Fer2 Status: True Positive Pfam 8_8 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 -Fer2 Status: True Positive Pfam 55_13 FER_CAPAN -1 55 130 Pfam +Fer2 Status: True Positive Pfam 55_13 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 -Fer2 Status: True Positive Pfam 55_13 FER1_SOLLC -1 55 130 Pfam +Fer2 Status: True Positive Pfam 55_13 FER1_SOLLC -1 55 130 Pfam Ferredoxin_fold Status: True Positive FER1_SOLLC -1 45 140 Cath -Fer2 Status: True Positive Pfam 55_13 Q93XJ9_SOLTU -1 55 130 Pfam +Fer2 Status: True Positive Pfam 55_13 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 -Fer2 Status: True Positive Pfam 60_13 FER1_PEA -1 60 135 Pfam +Fer2 Status: True Positive Pfam 60_13 FER1_PEA -1 60 135 Pfam Ferredoxin_fold Status: True Positive FER1_PEA -1 50 145 Cath -Fer2 Status: True Positive Pfam 63_13 Q7XA98_TRIPR -1 63 138 Pfam +Fer2 Status: True Positive Pfam 63_13 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 -Fer2 Status: True Positive Pfam 59_13 FER1_MESCR -1 59 134 Pfam +Fer2 Status: True Positive Pfam 59_13 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 -Fer2 Status: True Positive Pfam 58_13 FER1_SPIOL -1 58 133 Pfam +Fer2 Status: True Positive Pfam 58_13 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 -Fer2 Status: True Positive Pfam 8_8 FER3_RAPSA -1 8 83 Pfam +Fer2 Status: True Positive Pfam 8_8 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 -Fer2 Status: True Positive Pfam 8_8 FER_BRANA -1 8 83 Pfam +Fer2 Status: True Positive Pfam 8_8 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 -Fer2 Status: True Positive Pfam 60_13 FER2_ARATH -1 60 135 Pfam +Fer2 Status: True Positive Pfam 60_13 FER2_ARATH -1 60 135 Pfam Ferredoxin_fold Status: True Positive FER2_ARATH -1 50 145 Cath -Fer2 Status: True Positive Pfam 60_11 Q93Z60_ARATH -1 60 118 Pfam +Fer2 Status: True Positive Pfam 60_11 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 -Fer2 Status: True Positive Pfam 60_13 FER1_MAIZE -1 60 135 Pfam +Fer2 Status: True Positive Pfam 60_13 FER1_MAIZE -1 60 135 Pfam Ferredoxin_fold Status: True Positive FER1_MAIZE -1 50 145 Cath -Fer2 Status: True Positive Pfam 52_12 O80429_MAIZE -1 52 127 Pfam +Fer2 Status: True Positive Pfam 52_12 O80429_MAIZE -1 52 127 Pfam Ferredoxin_fold Status: True Positive O80429_MAIZE -1 42 137 Cath ENDGROUP uniprot diff --git a/examples/formComplete.html b/examples/formComplete.html index 91d94f8..9e89990 100644 --- a/examples/formComplete.html +++ b/examples/formComplete.html @@ -24,9 +24,7 @@
    -
    -

    Quick Links to jars for example:
    jalviewApplet.jar and JmolApplet.jar -

    +
    @@ -35,7 +33,7 @@

    Using the Javascript API to fill out forms using data from JalviewLite
    Click the Javascript buttons below to interact with the Applet instance on the page.

    -View the source in your browser to see how it has been done.
    + View the source here to see how it has been done (If the link doesn't work on your browser try going to this page and viewing the page source manually).
    View the full JalviewLite API documentation. diff --git a/examples/groovy/featureCounter.groovy b/examples/groovy/featureCounter.groovy index 08d038d..a16d8bb 100644 --- a/examples/groovy/featureCounter.groovy +++ b/examples/groovy/featureCounter.groovy @@ -5,8 +5,15 @@ 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 - * 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. + * + * To try: + * 1. load uniref50.fa from the examples folder + * 2. load features onto it from from examples/exampleFeatures.txt + * 3. Open this script in the Groovy console. + * 4. Either execute this script from the console, or via Calculate->Run Groovy Script + + * To explore further, try changing this script to count other kinds of occurrences of + * residue and sequence features at columns in an alignment. */ /* @@ -42,14 +49,15 @@ def hasPfam = { features -> } /* - * Closure that counts residues with a Pfam feature annotation + * Closure that computes an annotation based on + * presence of particular residues and features * Parameters are * - the name (label) for the alignment annotation * - the description (tooltip) for the annotation * - a closure (groovy function) that tests whether to include a residue * - a closure that tests whether to increment count based on sequence features */ -def getColumnCounter = { name, desc, residueTester, featureCounter -> +def getColumnCounter = { name, desc, acceptResidue, acceptFeatures -> [ getName: { name }, getDescription: { desc }, @@ -58,9 +66,9 @@ def getColumnCounter = { name, desc, residueTester, featureCounter -> count: { res, feats -> def c = 0 - if (residueTester.call(res)) + if (acceptResidue.call(res)) { - if (featureCounter.call(feats)) + if (acceptFeatures.call(feats)) { c++ } @@ -71,12 +79,12 @@ def getColumnCounter = { name, desc, residueTester, featureCounter -> } /* - * Define annotation that counts any residue with Pfam domain annotation + * Define an annotation row that counts any residue with Pfam domain annotation */ def pfamAnnotation = getColumnCounter("Pfam", "Count of residues with Pfam domain annotation", {true}, hasPfam) /* - * Define annotation that counts charged residues with Pfam domain annotation + * Define an annotation row that counts charged residues with Pfam domain annotation */ def chargedPfamAnnotation = getColumnCounter("Pfam charged", "Count of charged residues with Pfam domain annotation", isCharged, hasPfam) diff --git a/examples/groovy/printtitle.groovy b/examples/groovy/printtitle.groovy index 813df63..b3387ea 100644 --- a/examples/groovy/printtitle.groovy +++ b/examples/groovy/printtitle.groovy @@ -19,14 +19,17 @@ * The Jalview Authors are detailed in the 'AUTHORS' file. */ // do something groovy in jalview -print "Hello World.\n"; -def alf = Jalview.getAlignFrames(); +println "Hello World.\n" +println "First sequence is " + currentAlFrame.viewport.alignment.getSequenceAt(0).getDisplayId(true) + +def alf = Jalview.getAlignFrames() for (ala in alf) { // ala is an jalview.gui.AlignFrame object - print ala.getTitle()+"\n"; + println ala.getTitle() // get the parent jalview.datamodel.Alignment from the alignment viewport - def alignment = ala.viewport.alignment; + def alignment = ala.viewport.alignment // get the first sequence from the jalview.datamodel.Alignment object - def seq = alignment.getSequenceAt(0); + def seq = alignment.getSequenceAt(0) } +Jalview.quit() diff --git a/examples/javascriptLaunch.html b/examples/javascriptLaunch.html index e161ad6..35b1d81 100644 --- a/examples/javascriptLaunch.html +++ b/examples/javascriptLaunch.html @@ -21,12 +21,7 @@
    -
    -
    -

    Quick Links to jars for example:
    jalviewApplet.jar and JmolApplet.jar -

    -
    @@ -120,7 +115,7 @@ archive="jalviewApplet.jar,JmolApplet-14.2.14_2015.06.11.jar,java-json.jar,json_
    -

    Javascript Launch Button

    The button below demonstrates how JalviewLite can be launched via a javascript action.

    +

    Javascript Launch Button

    The button below demonstrates how JalviewLite can be launched via a javascript action. View the source here to see how it has been done (If the link doesn't work on your browser try going to this page and viewing the page source manually).

    diff --git a/examples/linkedapplets_ng.html b/examples/linkedapplets_ng.html index 0449c0b..5890515 100644 --- a/examples/linkedapplets_ng.html +++ b/examples/linkedapplets_ng.html @@ -22,9 +22,7 @@
    -
    -

    Quick Links to jars for example:
    jalviewApplet.jar and JmolApplet.jar -

    +
    @@ -36,7 +34,7 @@

    JalviewLite Linked Applets Demo

    The two applets below use JalviewLite's javascript API to exchange events about the currently selected region and mouse position in the alignment. -

    + View the source here to see how it has been done (If the link doesn't work on your browser try going to this page and viewing the page source manually).

    Description: Fer2 Status: True Positive Pfam 8_83 FER_CAPAA -1 8 83 Pfam +Description: Fer2 Status: True Positive Pfam 8_83 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 -Description: Fer2 Status: True Positive Pfam 55_130 FER_CAPAN -1 55 130 Pfam +Description: Fer2 Status: True Positive Pfam 55_130 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 -Description: Fer2 Status: True Positive Pfam 55_130 FER1_SOLLC -1 55 130 Pfam +Description: Fer2 Status: True Positive Pfam 55_130 FER1_SOLLC -1 55 130 Pfam Evidence: EI4 Q93XJ9_SOLTU -1 1 48 SIGNAL -Description: Fer2 Status: True Positive Pfam 55_130 Q93XJ9_SOLTU -1 55 130 Pfam +Description: Fer2 Status: True Positive Pfam 55_130 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 -Description: Fer2 Status: True Positive Pfam 60_135 FER1_PEA -1 60 135 Pfam -Description: Fer2 Status: True Positive Pfam 63_138 Q7XA98_TRIPR -1 63 138 Pfam +Description: Fer2 Status: True Positive Pfam 60_135 FER1_PEA -1 60 135 Pfam +Description: Fer2 Status: True Positive Pfam 63_138 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 -Description: Fer2 Status: True Positive Pfam 59_134 FER1_MESCR -1 59 134 Pfam +Description: Fer2 Status: True Positive Pfam 59_134 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 -Description: Fer2 Status: True Positive Pfam 58_133 FER1_SPIOL -1 58 133 Pfam +Description: Fer2 Status: True Positive Pfam 58_133 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 -Description: Fer2 Status: True Positive Pfam 8_83 FER3_RAPSA -1 8 83 Pfam +Description: Fer2 Status: True Positive Pfam 8_83 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 -Description: Fer2 Status: True Positive Pfam 60_135 FER1_ARATH -1 60 135 Pfam +Description: Fer2 Status: True Positive Pfam 60_135 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 -Description: Fer2 Status: True Positive Pfam 8_83 FER_BRANA -1 8 83 Pfam +Description: Fer2 Status: True Positive Pfam 8_83 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 -Description: Fer2 Status: True Positive Pfam 60_135 FER2_ARATH -1 60 135 Pfam -Description: Fer2 Status: True Positive Pfam 60_118 Q93Z60_ARATH -1 60 118 Pfam +Description: Fer2 Status: True Positive Pfam 60_135 FER2_ARATH -1 60 135 Pfam +Description: Fer2 Status: True Positive Pfam 60_118 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 -Description: Fer2 Status: True Positive Pfam 60_135 FER1_MAIZE -1 60 135 Pfam -Description: Fer2 Status: True Positive Pfam 52_127 O80429_MAIZE -1 52 127 Pfam +Description: Fer2 Status: True Positive Pfam 60_135 FER1_MAIZE -1 60 135 Pfam +Description: Fer2 Status: True Positive Pfam 52_127 O80429_MAIZE -1 52 127 Pfam ENDGROUP uniprot diff --git a/examples/testdata/simpleGff3.gff b/examples/testdata/simpleGff3.gff index 34b64ee..614b440 100644 --- a/examples/testdata/simpleGff3.gff +++ b/examples/testdata/simpleGff3.gff @@ -24,4 +24,3 @@ seq1 exonerate:protein2genome:local similarity 9 11 3652 - . alignment_id 0 ; Qu ACTACGACACGACGACGACGACG >seq2 CDEQEATGTQDAQEQAQC - diff --git a/examples/testdata/test.html b/examples/testdata/test.html index 229596c..1e41232 100644 --- a/examples/testdata/test.html +++ b/examples/testdata/test.html @@ -26,4 +26,4 @@ subCatContainer.scroll( function() { subCatContainer.scrollTop($(this).scrollTop()); }); - \ No newline at end of file + diff --git a/help/help.jhm b/help/help.jhm index ce98085..53b09e5 100755 --- a/help/help.jhm +++ b/help/help.jhm @@ -144,6 +144,10 @@ + + + + diff --git a/help/helpTOC.xml b/help/helpTOC.xml index fe8e1a9..c4476fd 100755 --- a/help/helpTOC.xml +++ b/help/helpTOC.xml @@ -23,11 +23,9 @@ - - - - - + + + @@ -107,7 +105,7 @@ - + @@ -133,8 +131,7 @@ - - + diff --git a/help/html/calculations/consensus.html b/help/html/calculations/consensus.html index 0a0214e..c870887 100644 --- a/help/html/calculations/consensus.html +++ b/help/html/calculations/consensus.html @@ -55,6 +55,11 @@ Normalise Consensus Logo to scale all columns of the logo to the same height. +

    + Group Consensus
    + If sequence groups have been defined, then selecting option 'Group Consensus' in the Annotations menu will + result in Consensus being calculated for each group, as well as the alignment as a whole. +

    cDNA Consensus

    diff --git a/help/html/calculations/conservation.html b/help/html/calculations/conservation.html index df8b5ff..9cb8ce1 100755 --- a/help/html/calculations/conservation.html +++ b/help/html/calculations/conservation.html @@ -65,11 +65,16 @@ (e.g. !proline).

    - Colouring an alignment by conservation
    + Colouring an alignment by conservation
    Conservation scores can be used to colour an alignment. This is explained further in the help page for conservation colouring.

    +

    + Group conservation
    + If sequence groups have been defined, then selecting option 'Group Conservation' in the Annotations menu will + result in Conservation being calculated for each group, as well as the alignment as a whole. +

    diff --git a/help/html/calculations/treeviewer.html b/help/html/calculations/treeviewer.html index b45f8fe..c91fe1b 100755 --- a/help/html/calculations/treeviewer.html +++ b/help/html/calculations/treeviewer.html @@ -120,7 +120,7 @@
  • Associate Leaves with ...

    Only visible when there are multiple views of the same alignment to show and edit which alignment views are associated with the leaves of the displayed tree. diff --git a/help/html/features/annotation.html b/help/html/features/annotation.html index a645630..7e52c46 100755 --- a/help/html/features/annotation.html +++ b/help/html/features/annotation.html @@ -48,6 +48,25 @@ alignment window or loaded from the alignment's file menu. href="../webServices/proteinDisorder.html">disordered region prediction methods.

    +

    Sequence Group Annotation +

    +

    + If sequence groups are defined, Conservation + and Consensus annotation can be enabled + for each group from the Annotations menu, or can + be imported from a Jalview Annotations file. +

    +

    Sequence Selection from Annotation +

    +

    + Sequences associated with sequence (or sequence group) annotations can be selected by + double-clicking the annotation label with these key combinations: +

      +
    • double-click - Select associated sequences (replaces current selection)
    • +
    • shift double-click - add sequences to selection
    • +
    • Ctrl (Mac CMD) double-click - toggles inclusion of associated sequences in the current selection
    • +
    + Note this also works in combination with manual sequence selection in the alignment.

    Interactive Alignment Annotation

    Annotation rows are added using the Annotation Label diff --git a/help/html/features/annotationsFormat.html b/help/html/features/annotationsFormat.html index 545f0c1..744370b 100755 --- a/help/html/features/annotationsFormat.html +++ b/help/html/features/annotationsFormat.html @@ -110,7 +110,7 @@ followed by a description 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 sequence feature's + the same way as a sequence feature's label), providing the text is enclosed in an <html/> tag.

      Please note: URL links embedded in HTML descriptions are diff --git a/help/html/features/bioJsonFormat.html b/help/html/features/bioJsonFormat.html index f8f87dc..cb7ccc0 100644 --- a/help/html/features/bioJsonFormat.html +++ b/help/html/features/bioJsonFormat.html @@ -20,7 +20,7 @@ * The Jalview Authors are detailed in the 'AUTHORS' file. --> -BioJSON in Jalviwe +BioJSON in Jalview

      diff --git a/help/html/features/chimera.html b/help/html/features/chimera.html index 39dff75..0569513 100644 --- a/help/html/features/chimera.html +++ b/help/html/features/chimera.html @@ -181,7 +181,7 @@ Colours each residue in the structure with the colour of its corresponding residue in the associated sequence as rendered in the associated alignment views, including any - Uniprot sequence features or region colourings.
      Pick + UniProt sequence features or region colourings.
      Pick which of the associated alignment views are used to colour the structures using the View→Colour by .. sub menu. diff --git a/help/html/features/clarguments.html b/help/html/features/clarguments.html index 63a14af..1d6f62d 100644 --- a/help/html/features/clarguments.html +++ b/help/html/features/clarguments.html @@ -96,6 +96,13 @@

  • + + + diff --git a/help/html/menus/alignmentMenu.html b/help/html/menus/alignmentMenu.html index ce339cc..5739797 100755 --- a/help/html/menus/alignmentMenu.html +++ b/help/html/menus/alignmentMenu.html @@ -33,7 +33,7 @@
    • Fetch Sequence
      Shows a dialog window in which you can retrieve known ids from - Uniprot, EMBL, EMBLCDS, PFAM, Rfam, or PDB database using + UniProt, EMBL, EMBLCDS, PFAM, Rfam, or PDB database using Web Services provided by the European Bioinformatics Institute. See Sequence Fetcher diff --git a/help/html/menus/alwfile.html b/help/html/menus/alwfile.html index 10d510d..bf1ba90 100755 --- a/help/html/menus/alwfile.html +++ b/help/html/menus/alwfile.html @@ -29,7 +29,7 @@

    @@ -233,7 +271,7 @@ region export in flat file generation
  • Export alignment views for display with the BioJS MSAViewer
  • + href="http://msa.biojs.net/">BioJS MSAViewer
  • Export scrollable SVG in HTML page
  • Optional embedding of BioJSON data when exporting @@ -718,7 +756,7 @@ Certum to the Jalview open source project).
  • -
  • Jalview SRS links replaced by Uniprot and EBI-search +
  • Jalview SRS links replaced by UniProt and EBI-search
  • Output in Stockholm format
  • Allow import of data from gzipped files
  • @@ -1073,8 +1111,8 @@ current built in colourscheme is saved as new scheme
  • AlignFrame->Save in application pops up save dialog for valid filename/format
  • -
  • Cannot view associated structure for Uniprot sequence
  • -
  • PDB file association breaks for Uniprot sequence +
  • Cannot view associated structure for UniProt sequence
  • +
  • PDB file association breaks for UniProt sequence P37173
  • Associate PDB from file dialog does not tell you which sequence is to be associated with the file
  • @@ -1392,10 +1430,7 @@ between different screens.
  • New preference items for sequence ID tooltip and consensus annotation
  • -
  • Client to submit sequences and IDs to Envision2 - Workflows -
  • +
  • Client to submit sequences and IDs to Envision2 Workflows
  • Vamsas Capabilities
    • Improved VAMSAS synchronization (Jalview archive @@ -1652,7 +1687,7 @@
    • Save works when Jalview project is default format
    • Save as dialog opened if current alignment format is not a valid output format
    • -
    • Uniprot canonical names introduced for both das and +
    • UniProt canonical names introduced for both das and vamsas
    • Histidine should be midblue (not pink!) in Zappo
    • error messages passed up and output when data read @@ -1681,7 +1716,7 @@ due to null pointer exceptions
    • Secondary structure lines are drawn starting from first column of alignment
    • -
    • Uniprot XML import updated for new schema release in +
    • UniProt XML import updated for new schema release in July 2008
    • Sequence feature to sequence ID match for Features file is case-insensitive
    • @@ -1825,7 +1860,7 @@
    • Re-instated Zoom function for PCA
    • Sequence descriptions conserved in web service analysis results -
    • Uniprot ID discoverer uses any word separated by +
    • UniProt ID discoverer uses any word separated by ∣
    • WsDbFetch query/result association resolved
    • Tree leaf to sequence mapping improved diff --git a/help/html/webServices/AACon.html b/help/html/webServices/AACon.html index 5dd4472..6d4a461 100644 --- a/help/html/webServices/AACon.html +++ b/help/html/webServices/AACon.html @@ -32,7 +32,7 @@ Function, and Genetics 43(2): 227-241. PubMed or available on the Valdar Group publications page), but the SMERFs score was developed later and described by Manning et al. in 2008 ( The Sequence Identification Process
      The method of accession id discovery is derived from the method which - earlier Jalview versions used for Uniprot sequence feature retrieval, - and was originally restricted to the identification of valid Uniprot + earlier Jalview versions used for UniProt sequence feature retrieval, + and was originally restricted to the identification of valid UniProt accessions.
      Essentially, Jalview will try to retrieve records from a subset of the databases accessible by the
      REST 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 JABAWS, however, it is possible for anyone to host Jalview web services.

      diff --git a/help/html/webServices/msaclient.html b/help/html/webServices/msaclient.html index b685576..6266036 100644 --- a/help/html/webServices/msaclient.html +++ b/help/html/webServices/msaclient.html @@ -39,13 +39,13 @@ from the input
    • realignment - where any aligned sequences will be used by the service to construct a profile based alignment of the - remaining unaligned sequences.
    • + remaining unaligned sequences
    JABAWS Alignment services
    Most alignment services are provided by the JABAWS framework, 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 'Edit Settings And Run ..' dialog box. @@ -58,7 +58,7 @@
    • Clustal Omega and Clustal W (version 2.0.12)
    • -
    • Mafft +
    • Mafft (version 6.8.57b)
    • Muscle (version 3.8.31)
    • diff --git a/help/html/webServices/newsreader.html b/help/html/webServices/newsreader.html index ee69290..1f2b7c9 100644 --- a/help/html/webServices/newsreader.html +++ b/help/html/webServices/newsreader.html @@ -19,7 +19,7 @@ * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. --> -Jalview Desktop RSS News Reader +

      @@ -35,16 +35,21 @@

      The news reader will be launched automatically when you start the Desktop if new items are available. Should you want to browse older items, however, you can open it manually from the 'Jalview - news reader' option in the Desktop's 'Tools' menu.

      - Snapshot of the Jalview Desktop's RSS reader -

      + news reader' option in the Desktop's 'Tools' menu.


      +
      Snapshot of the Jalview Desktop's RSS reader
      + +

      The Jalview news reader was introduced in Jalview version 2.7. Its implementation is based on JSwingReader.

      +
      + If you need to prevent the news-reader opening, then add the + command-line parameter + '-nonews' to Jalview's command-line launch. + diff --git a/help/html/webServices/proteinDisorder.html b/help/html/webServices/proteinDisorder.html index 57e6698..2c98139 100644 --- a/help/html/webServices/proteinDisorder.html +++ b/help/html/webServices/proteinDisorder.html @@ -40,7 +40,7 @@ >sequence features and sequence associated alignment annotation rows. Features display is controlled from - the Feature Settings + the Feature Settings dialog box. Clicking on the ID for a disorder prediction annotation row will highlight or select (if double clicked) the associated sequence for that row. You can also use the Sequence diff --git a/help/html/webServices/urllinks.html b/help/html/webServices/urllinks.html index de0f8cd..7a23d58 100644 --- a/help/html/webServices/urllinks.html +++ b/help/html/webServices/urllinks.html @@ -83,7 +83,7 @@ Please Note:
      • The regular expressions supported by Jalview are those - provided by the Stevesoft + provided by the Stevesoft javaregex package.
      • Some characters must be escaped when specifying them as diff --git a/help/html/whatsNew.html b/help/html/whatsNew.html index 19be76f..407eca3 100755 --- a/help/html/whatsNew.html +++ b/help/html/whatsNew.html @@ -27,61 +27,18 @@ What's new ?

        - Jalview 2.9.0b2 is a bug fix release for Jalview 2.9. - The release of Jalview 2.9 in September 2015 included - a multitude of bug fixes and minor improvements (both small, and - rather big!), it also brings major new capabilities for codon-level - analysis of protein alignments and the retrieval and manipulation of - structural data.

        For the patches since version 2.9 was released, see the - Jalview 2.9.0b2 Release Notes. + Jalview 2.10 is the next major release in the Jalview 2 series. Full + details are in the Jalview + 2.10 Release Notes, but the highlights are below.

        - Highlights in Jalview 2.9 + Highlights in Jalview 2.10

          -
        • Visualisation, editing and analysis of - cDNA and Protein alignments
          A new Split View window allows linked - protein and nucleotide sequence alignments to be viewed, edited, - and analysed as one.
          cDNA alignments can also be - reconstructed from protein alignments calculated by Jalview's web - services, and update in response to edits in the amino acid view.
          To - start experimenting with cDNA/Protein analysis, jut drop a file - containing cDNA sequences which code for proteins in an existing - alignment, and Jalview will do the rest.
        • -
        • Enhanced Integration of UCSF Chimera
          Jalview - 2.9 provides full support for the use of Chimera to view 3D - structures linked to alignment views in the Jalview Desktop. We've - also included support for saving Chimera sessions in Jalview - project files.
          Jalview and Chimera communicate using local - web server connections, which may cause firewall alerts on some - systems, but has the advantage of allowing bidirectional - communication. Communication between Jalview and Chimera is now - much more responsive, and selected regions in Chimera are now - shown as highlighted regions in the Jalview desktop.
        • -
        • Interactive querying of the PDBe
          Jalview - users can now browse and retrieve 3D structure data from the PDB - via the PDBe - Search API (Gutmanas - et al 2014). Developed in collaboration with the PDBe group at - EMBL-EBI, the interface allows both structured and free-text - queries to be performed, and allows automatic selection of the - most relevant structures for an alignment acording to a variety of - criteria.
        • -
        • Improved support for RNA visualisation
          Jalview - 2.9 integrates the latest version of the VARNA RNA Viewer, and VARNA views - can also now be stored in Jalview projects. We've also dealt with - a number of lingering bugs in the VARNA/Jalview interface, - including the loss of pseudoknots when RNA secondary structure is - shown VARNA.
        • -
        • Protein Secondary Structure predictions - with JPred4
          Jalview includes a number of new features for - working with secondary structure predictions from the JPred4 - server. These include new popup menu actions to automatically hide insertions and highlight - mutations in an alignment with respect to a Reference - Sequence. Jalview 2.9's new scrollable - SVG HTML export was also developed specifically for the JPred4 - server.
        • +
        • Ensembl sequence fetcher. Annotated Genes, transcripts and + proteins can be retrieved via Jalview's new Ensembl REST client.
        • +
        • Improved sequence/structure mappings. + Jalview now utilises the PDBe's SIFTS database (at EMBL-EBI) to + match PDB data positions in UniProt sequences.
        diff --git a/lib/Jmol-14.2.14_2015.06.11.jar b/lib/Jmol-14.2.14_2015.06.11.jar index f77f5f0..1470745 100644 Binary files a/lib/Jmol-14.2.14_2015.06.11.jar and b/lib/Jmol-14.2.14_2015.06.11.jar differ diff --git a/lib/VARNAv3-93.jar b/lib/VARNAv3-93.jar index b73b58d..9d41f6b 100644 Binary files a/lib/VARNAv3-93.jar and b/lib/VARNAv3-93.jar differ diff --git a/resources/embl_mapping.xml b/resources/embl_mapping.xml index ccbde5e..01b921a 100644 --- a/resources/embl_mapping.xml +++ b/resources/embl_mapping.xml @@ -22,35 +22,61 @@ - + - - + - - - + + + - - + + + + + + + + + + + + + + + + + + + + - - + + - - + + - + + + + @@ -60,19 +86,13 @@ - + - - - - - - @@ -81,25 +101,25 @@ + + + - + - - - - + - + @@ -113,40 +133,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/fts/pdb_data_columns.txt b/resources/fts/pdb_data_columns.txt index 95f2dd1..5ac50bb 100644 --- a/resources/fts/pdb_data_columns.txt +++ b/resources/fts/pdb_data_columns.txt @@ -1,3 +1,24 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ + pdb_data_columns # _group.id @@ -6,9 +27,9 @@ _group.sort_order g1;Quality Measures;1 g2;Cross References;2 g3;Names & Taxonomy;3 -g4;Procedures & Softwares;4 +g4;Procedures & Software;4 g5;Date Of;5 -g6;Miscellenous;6 +g6;Miscellaneous;6 # _data_column.primary_key;pdb_id _data_column.default_response_page_size;100 @@ -16,7 +37,7 @@ _data_column.default_response_page_size;100 _data_column.name _data_column.code _data_column.group_id -_data_column.data_type +_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 @@ -35,17 +56,17 @@ 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;g1;50;150;85;false;false -Resolution;resolution;Double;g1;50;150;85;true;false -Data Quality;data_quality;Double;g1;50;150;85;false;false -Overall Quality;overall_quality;Double;g1;50;150;85;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 -Experimental Method;experimental_method;String;g4;50;400;95;false;false 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 @@ -58,10 +79,10 @@ 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;g1;50;150;85;false;false -Pivot Resolution;pivot_resolution;Double;g1;50;150;85;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 +Max observed residues;max_observed_residues;Integer|F;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 @@ -86,7 +107,7 @@ 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;g1;50;150;85;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 @@ -103,7 +124,7 @@ 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;g6;50;400;95;false;false -Version;_version_;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 index 6c78c16..f506648 100644 --- a/resources/fts/uniprot_data_columns.txt +++ b/resources/fts/uniprot_data_columns.txt @@ -1,3 +1,24 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ + uniprot_data_columns # _group.id @@ -8,7 +29,7 @@ g2;Cross References;2 g3;Names & Taxonomy;1 g4;Procedures & Softwares;4 g5;Date Of;5 -g6;Miscellenous;6 +g6;Miscellaneous;6 g7;Sequences;7 g8;Function;8 g9;Interaction;9 @@ -35,16 +56,16 @@ _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|name;String;g3;100;150;105;true;true -Protein names;protein names|protein name;String;g3;300;1500;500;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;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 +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 @@ -55,9 +76,9 @@ 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;g7;50;100;65;true;true -Mass;mass;String;g7;50;100;80;false;true -Sequence;sequence;String;g7;50;1000;95;false;true +Length;length;int|T|0;g7;50;100;65;true;true +Mass;mass;int|T|0;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 @@ -83,15 +104,15 @@ 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 +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;String;g6;50;1000;95;false;true +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; +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;true +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 @@ -131,7 +152,7 @@ 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;comment(DOMAIN)|domain;String;g18;80;1000;95;false;true +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 diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 8dac5c6..f5b4f7a 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -38,7 +38,6 @@ action.cancel = Cancel action.create = Create action.update = Update action.delete = Delete -action.snapshot = Snapshot action.clear = Clear action.accept = Accept action.select_ddbb = --- Select Database --- @@ -121,7 +120,6 @@ action.save_as_default = Save as default action.save_as = Save as... action.save = Save action.cancel_fetch = Cancel Fetch -action.save_omit_hidden_columns = Save / Omit Hidden Regions action.change_font = Change Font action.change_font_tree_panel = Change Font (Tree Panel) action.colour = Colour @@ -137,21 +135,22 @@ action.show_group = Show Group action.fetch_db_references = Fetch DB References action.view_flanking_regions = Show flanking regions label.view_flanking_regions = Show sequence data either side of the subsequences involved in this alignment -label.str = Str: -label.seq = Seq: label.structures_manager = Structures Manager label.nickname = Nickname: label.url = URL: label.input_file_url = Enter URL or Input File -label.select_feature = Select feature: +label.select_feature = Select feature label.name = Name +label.name\: = Name: label.name_param = Name: {0} label.group = Group +label.group\: = Group: label.group_name = Group Name label.group_description = Group Description label.edit_group_name_description = Edit Group Name/Description label.colour = Colour: -label.description = Description: +label.description = Description +label.description\: = Description: label.start = Start: label.end = End: label.current_parameter_set_name = Current parameter set name: @@ -222,8 +221,8 @@ label.proteins = Proteins label.to_new_alignment = To New Alignment label.to_this_alignment = Add To This Alignment label.apply_colour_to_all_groups = Apply Colour To All Groups -label.modify_identity_thereshold = Modify Identity Threshold... -label.modify_conservation_thereshold = Modify Conservation Threshold... +label.modify_identity_threshold = Modify Identity Threshold... +label.modify_conservation_threshold = Modify Conservation Threshold... label.input_from_textbox = Input from textbox label.centre_column_labels = Centre column labels label.automatic_scrolling = Automatic Scrolling @@ -240,7 +239,6 @@ label.except_selected_sequences = All except selected sequences label.all_but_selected_region = All but Selected Region (Shift+Ctrl+H) label.selected_region = Selected Region label.all_sequences_columns = All Sequences and Columns -label.hide_insertions = Hide columns gapped for selection label.hide_selected_annotations = Hide selected annotations label.show_selected_annotations = Show selected annotations label.group_consensus = Group Consensus @@ -321,7 +319,6 @@ label.found_match_for = Found match for {0} label.font = Font: label.size = Size: label.style = Style: -label.enter_redundancy_threshold = Enter the redundancy threshold label.calculating = Calculating.... label.modify_conservation_visibility = Modify conservation visibility label.colour_residues_above_occurence = Colour residues above % occurence @@ -365,7 +362,6 @@ label.example = Example label.example_param = Example: {0} label.select_file_format_before_saving = You must select a file format before saving! label.file_format_not_specified = File format not specified -label.alignment_contains_hidden_columns = The Alignment contains hidden regions (hidden sequences/columns).\nDo you want to save only the visible alignment? label.couldnt_save_file = Couldn't save file: {0} label.error_saving_file = Error Saving File label.remove_from_default_list = Remove from default list? @@ -393,7 +389,7 @@ label.ignore_unmatched_dropped_files = Ignore unmatched dropped files? label.view_name_original = Original label.enter_view_name = Enter View Name label.enter_label = Enter label -label.enter_label_for_the_structure = Enter a label for the structure? +label.enter_label_for_the_structure = Enter a label for the structure label.pdb_entry_is_already_displayed = {0} is already displayed.\nDo you want to re-use this viewer ? label.map_sequences_to_visible_window = Map Sequences to Visible Window: {0} label.add_pdbentry_to_view = Do you want to add {0} to the view called\n{1}\n @@ -467,7 +463,6 @@ label.no_features_added_to_this_alignment = No Features added to this alignment! label.features_can_be_added_from_searches_1 = (Features can be added from searches or label.features_can_be_added_from_searches_2 = from Jalview / GFF features files) label.calculating_pca= Calculating PCA -label.reveal_columns = Reveal Columns label.jalview_cannot_open_file = Jalview can't open file label.jalview_applet = Jalview applet label.loading_data = Loading data @@ -475,7 +470,6 @@ label.memory_stats = Total Free Memory: {0} MB; Max Memory: {1} MB; {2} % label.calculating_tree = Calculating tree label.state_queueing = queuing label.state_running = running -label.state_complete = complete label.state_completed = finished label.state_job_cancelled = job cancelled!! label.state_job_error = job error! @@ -501,7 +495,6 @@ label.jmol_help = Jmol Help label.chimera_help = Chimera Help label.close_viewer = Close Viewer label.confirm_close_chimera = This will close Jalview''s connection to {0}.
        Do you want to close the Chimera window as well? -label.chimera_help = Chimera Help label.all = All label.sort_by = Sort alignment by label.sort_by_score = Sort by Score @@ -521,15 +514,14 @@ label.reset_min_max_colours_to_defaults = Reset min and max colours to defaults label.align_structures_using_linked_alignment_views = Align structures using {0} linked alignment views label.connect_to_session = Connect to session {0} label.threshold_feature_display_by_score = Threshold the feature display by score. -label.threshold_feature_no_thereshold = No Threshold -label.threshold_feature_above_thereshold = Above Threshold -label.threshold_feature_below_thereshold = Below Threshold -label.adjust_thereshold = Adjust threshold +label.threshold_feature_no_threshold = No Threshold +label.threshold_feature_above_threshold = Above Threshold +label.threshold_feature_below_threshold = Below Threshold +label.adjust_threshold = Adjust threshold label.toggle_absolute_relative_display_threshold = Toggle between absolute and relative display threshold. label.display_features_same_type_different_label_using_different_colour = Display features of the same type with a different label using a different colour. (e.g. domain features) label.select_colour_minimum_value = Select Colour for Minimum Value label.select_colour_maximum_value = Select Colour for Maximum Value -label.open_new_jmol_view_with_all_structures_associated_current_selection_superimpose_using_alignment = Open a new structure viewer with all structures associated with the current selection and superimpose them using the alignment. label.open_url_param = Open URL {0} label.open_url_seqs_param = Open URL ({0}..) ({1} seqs) label.load_pdb_file_associate_with_sequence = Load a PDB file and associate it with sequence {0} @@ -584,8 +576,8 @@ label.histogram = Histogram label.logo = Logo label.non_positional_features = List Non-positional Features label.database_references = List Database References -label.share_selection_across_views = Share selection across views -label.scroll_highlighted_regions = Scroll to highlighted regions +#label.share_selection_across_views = Share selection across views +#label.scroll_highlighted_regions = Scroll to highlighted regions label.gap_symbol = Gap Symbol label.prot_alignment_colour = Protein Alignment Colour label.nuc_alignment_colour = Nucleotide Alignment Colour @@ -668,20 +660,12 @@ label.cut_paste = Cut'n'Paste label.adjusting_parameters_for_calculation = Adjusting parameters for existing Calculation label.2d_rna_structure_line = 2D RNA {0} (alignment) label.2d_rna_sequence_name = 2D RNA - {0} -label.edit_name_and_description_current_group = Edit name and description of current group. -label.view_structure_for = View structure for {0} -label.view_all_structures = View all {0} structures. -label.view_all_representative_structures = View all {0} representative structures. -label.open_new_jmol_view_with_all_representative_structures_associated_current_selection_superimpose_using_alignment = Opens a new structure viewer with all representative structures\nassociated with the current selection\nsuperimposed with the current alignment. -label.associate_structure_with_sequence = Associate Structure with Sequence +label.edit_name_and_description_current_group = Edit name and description of current group label.from_file = From File -label.enter_pdb_id = Enter PDB Id -label.discover_pdb_ids = Discover PDB IDs +label.enter_pdb_id = Enter PDB Id (or pdbid:chaincode) label.text_colour = Text Colour action.set_text_colour = Text Colour... label.structure = Structure -label.view_structure = View Structure -label.view_protein_structure = View Protein Structure label.show_pdbstruct_dialog = 3D Structure Data... label.view_rna_structure = VARNA 2D Structure label.clustalx_colours = Clustalx colours @@ -709,7 +693,6 @@ label.translate_cDNA = Translate as cDNA label.reverse = Reverse label.reverse_complement = Reverse Complement label.linked_view_title = Linked CDS and protein view -label.align = Align label.extract_scores = Extract Scores label.get_cross_refs = Get Cross-References label.sort_alignment_new_tree = Sort Alignment With New Tree @@ -721,7 +704,6 @@ label.use_registry = Use Registry label.add_local_source = Add Local Source label.set_as_default = Set as Default label.show_labels = Show labels -label.background_colour = Background Colour action.background_colour = Background Colour... label.associate_nodes_with = Associate Nodes With label.jalview_pca_calculation = Jalview PCA Calculation @@ -790,7 +772,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 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} @@ -814,7 +796,6 @@ label.services_at = Services at {0} label.rest_client_submit = {0} using {1} label.fetch_retrieve_from =Retrieve from {0} label.fetch_retrieve_from_all_sources = Retrieve from all {0} sources in {1}
        First is :{2} -#label.feature_settings_click_drag = Click/drag feature types up or down to change render order.
        Double click to select columns containing feature in alignment/current selection
        Pressing Alt will select columns outside features rather than inside
        Pressing Shift to modify current selection (rather than clear current selection)
        Press CTRL or Command/Meta to toggle columns in/outside features
        label.feature_settings_click_drag = Drag up or down to change render order.
        Double click to select columns containing feature. label.transparency_tip = Adjust transparency to 'see through' feature colours. label.opt_and_params_further_details = see further details by right-clicking @@ -830,17 +811,10 @@ label.user_preset = User Preset label.service_preset = Service Preset label.run_with_preset = Run {0} with preset label.view_service_doc_url = View {1} -label.submit_sequence = Submit {0} {1} {2} {3} to
        {4} action.by_title_param = By {0} -label.alignment = Alignment -label.secondary_structure_prediction = Secondary Structure Prediction -label.sequence_database_search = Sequence Database Search -label.analysis = Analysis -label.protein_disorder = Protein Disorder label.source_from_db_source = Sources from {0} label.from_msname = from {0} label.superpose_with = Superpose with -action.do = Do label.scale_label_to_column = Scale Label to Column label.add_new_row = Add New Row label.edit_label_description = Edit Label/Description @@ -884,7 +858,7 @@ label.service_url = Service URL label.copied_sequences = Copied sequences label.cut_sequences = Cut Sequences label.conservation_colour_increment = Conservation Colour Increment ({0}) -label.percentage_identity_thereshold = Percentage Identity Threshold ({0}) +label.percentage_identity_threshold = Percentage Identity Threshold ({0}) label.error_unsupported_owwner_user_colour_scheme = Unsupported owner for User Colour scheme dialog label.save_alignment_to_file = Save Alignment to file label.save_features_to_file = Save Features to File @@ -900,7 +874,6 @@ label.save_vamsas_document_archive = Save Vamsas Document Archive label.saving_vamsas_doc = Saving VAMSAS Document to {0} label.load_feature_colours = Load Feature Colours label.save_feature_colours = Save Feature Colour Scheme -label.dataset_for = {0} Dataset for {1} label.select_startup_file = Select startup file label.select_default_browser = Select default web browser label.save_tree_as_newick = Save tree as newick file @@ -928,15 +901,9 @@ error.null_from_clone1 = Null from clone1! error.implementation_error_sortbyfeature = Implementation Error - sortByFeature method must be one of FEATURE_SCORE, FEATURE_LABEL or FEATURE_DENSITY. error.not_yet_implemented = Not yet implemented error.unknown_type_dna_or_pep = Unknown Type {0} - dna or pep are the only allowed values. -error.implementation_error_dont_know_thereshold_annotationcolourgradient = Implementation error: don't know about threshold setting for current AnnotationColourGradient. -error.implementation_error_embeddedpopup_not_null = Implementation error - embeddedPopup must be non-null -error.invalid_colour_for_mycheckbox = Invalid color for MyCheckBox -error.implementation_error_unrecognised_render_object_for_features_type = Implementation Error: Unrecognised render object {0} for features of type {1} -error.implementation_error_unsupported_feature_colour_object = Implementation error: Unsupported feature colour object. +error.implementation_error_dont_know_threshold_annotationcolourgradient = Implementation error: don't know about threshold setting for current AnnotationColourGradient. error.invalid_separator_parameter = Invalid separator parameter - must be non-zero length error.alignment_cigararray_not_implemented = Alignment(CigarArray) not yet implemented -error.weak_sequencei_equivalence_not_yet_implemented = Weak sequenceI equivalence not yet implemented. -error.implementation_error_can_only_make_alignmnet_from_cigararray = Implementation Error - can only make an alignment view from a CigarArray of sequences. error.empty_view_cannot_be_updated = empty view cannot be updated. error.mismatch_between_number_of_sequences_in_block = Mismatch between number of sequences in block {0} ({1}) and the original view ({2}) error.padding_not_yet_implemented = Padding not yet implemented @@ -958,21 +925,18 @@ error.not_yet_implemented_cigar_object_from_cigar_string = NOT YET Implemented: error.implementation_bug_cigar_operation = Implementation Bug. Cigar Operation {0} {1} not one of {2}, {3}, or {4}. error.implementation_error_for_new_cigar = Implementation error for new Cigar(SequenceI) error.implementation_error_cigar_seq_no_operations = Implementation error: {0}th sequence Cigar has no operations. -error.implementation_error_jmol_getting_data = Implementation error - Jmol seems to be still working on getting its data - report at http://issues.jalview.org/browse/JAL-1016 error.implementation_error_no_pdbentry_from_index = Implementation error - no corresponding pdbentry (for index {0}) to add sequences mappings to error.jmol_version_not_compatible_with_jalview_version = Jmol version {0} is not compatible with this version of Jalview. Report this problem at issues.jalview.org error.not_implemented_remove = Remove: Not implemented error.not_implemented_clone = Clone: Not implemented -error.implementation_error_chimera_getting_data = Implementation error - Chimera seems to be still working on getting its data - report at http://issues.jalview.org/browse/JAL-1016 error.call_setprogressbar_before_registering_handler = call setProgressBar before registering the progress bar's handler. label.cancelled_params = Cancelled {0} error.implementation_error_cannot_show_view_alignment_frame = Implementation error: cannot show a view from another alignment in an AlignFrame. -error.implementation_error_dont_know_about_thereshold_setting = Implementation error: don't know about threshold setting for current AnnotationColourGradient. +error.implementation_error_dont_know_about_threshold_setting = Implementation error: don't know about threshold setting for current AnnotationColourGradient. error.eps_generation_not_implemented = EPS Generation not yet implemented error.png_generation_not_implemented = PNG Generation not yet implemented error.try_join_vamsas_session_another = Trying to join a vamsas session when another is already connected error.invalid_vamsas_session_id = Invalid vamsas session id -error.implementation_error_cannot_create_groovyshell = Implementation Error. Cannot create groovyShell without Groovy on the classpath! label.groovy_support_failed = Jalview Groovy Support Failed label.couldnt_create_groovy_shell = Couldn't create the groovy Shell. Check the error log for the details of what went wrong. error.unsupported_version_calcIdparam = Unsupported Version for calcIdparam {0} @@ -987,7 +951,6 @@ error.setstatus_called_non_existent_job_pane = setStatus called for non-existent error.implementation_error_cannot_find_marshaller_for_param_set =Implementation error: Can't find a marshaller for the parameter set error.implementation_error_old_jalview_object_not_bound =IMPLEMENTATION ERROR: old jalview object is not bound ! ({0}) error.implementation_error_vamsas_doc_class_should_bind_to_type = Implementation Error: Vamsas Document Class {0} should bind to a {1} (found a {2}) -error.implementation_error_jalview_class_should_bind_to_type = Implementation Error: Jalview Class {0} should bind to a {1} (found a {2}) error.invalid_vamsas_rangetype_cannot_resolve_lists = Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice! error.implementation_error_maplist_is_null = Implementation error. MapList is null for initMapType. error.implementation_error_cannot_have_null_alignment = Implementation error: Cannot have null alignment property key @@ -1029,10 +992,11 @@ 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 containing features of type {2} across {3} sequence(s) +label.view_controller_toggled_marked = {0} {1} columns {2} features of type {3} across {4} sequence(s) label.toggled = Toggled label.marked = Marked -label.not = not +label.containing = containing +label.not_containing = not containing label.no_feature_of_type_found = No features of type {0} found. label.submission_params = Submission {0} label.empty_alignment_job = Empty Alignment Job @@ -1042,7 +1006,7 @@ label.pca_recalculating = Recalculating PCA label.pca_calculating = Calculating PCA label.select_foreground_colour = Choose foreground colour label.select_colour_for_text = Select Colour for Text -label.adjunst_foreground_text_colour_thereshold = Adjust Foreground Text Colour Threshold +label.adjunst_foreground_text_colour_threshold = Adjust Foreground Text Colour Threshold label.select_subtree_colour = Select Sub-Tree Colour label.create_new_sequence_features = Create New Sequence Feature(s) label.amend_delete_features = Amend/Delete Features for {0} @@ -1062,7 +1026,6 @@ exception.mismatched_unseen_closing_char = Mismatched (unseen) closing character exception.mismatched_closing_char = Mismatched closing character {0} exception.mismatched_opening_char = Mismatched opening character {0} at {1} exception.invalid_datasource_couldnt_obtain_reader = Invalid datasource. Could not obtain Reader -exception.index_value_not_in_range = {0}: Index value {1} not in range [0..{2}] exception.unterminated_cigar_string = Unterminated cigar string exception.unexpected_operation_cigar_string_pos = Unexpected operation {0} in cigar string (position {1} in {2} exception.couldnt_parse_responde_from_annotated3d_server = Couldn't parse response from Annotate3d server @@ -1090,7 +1053,6 @@ exception.ranml_problem_parsing_data = Problem parsing data as RNAML ({0}) exception.pfam_no_sequences_found = No sequences found (PFAM input) exception.stockholm_invalid_format = This file is not in valid STOCKHOLM format: First line does not contain '# STOCKHOLM' exception.couldnt_parse_sequence_line = Could not parse sequence line: {0} -exception.error_parsing_line = Error parsing {0} exception.unknown_annotation_detected = Unknown annotation detected: {0} {1} exception.couldnt_store_sequence_mappings = Couldn't store sequence mappings for {0} exception.matrix_too_many_iteration = Too many iterations in {0} (max is {1}) @@ -1098,7 +1060,6 @@ exception.browser_not_found = Exception in finding browser: {0} exception.browser_unable_to_locate = Unable to locate browser: {0} exception.invocation_target_exception_creating_aedesc = InvocationTargetException while creating AEDesc: {0} exception.illegal_access_building_apple_evt= IllegalAccessException while building AppleEvent: {0} -exception.instantiation_creating_aedesc = InstantiationException while creating AEDesc: {0} exception.unable_to_launch_url = Unable to launch URL: {0} exception.unable_to_create_internet_config = Unable to create an Internet Config instance: {0} exception.invocation_target_calling_url = InvocationTargetException while calling openURL: {0} @@ -1107,8 +1068,6 @@ exception.interrupted_launching_browser = InterruptedException while launching b exception.das_source_doesnt_support_sequence_command = Source {0} does not support the sequence command. exception.invalid_das_source = Invalid das source: {0} exception.ebiembl_retrieval_failed_on = EBI EMBL XML retrieval failed on {0}:{1} -label.no_embl_record_found = # No EMBL record retrieved for {0}:{1} -label.embl_successfully_parsed = # Successfully parsed the {0} queries into an Alignment exception.no_pdb_records_for_chain = No PDB Records for {0} chain {1} exception.unexpected_handling_rnaml_translation_for_pdb = Unexpected exception when handling RNAML translation of PDB data exception.couldnt_recover_sequence_properties_for_alignment = Couldn't recover sequence properties for alignment @@ -1199,8 +1158,8 @@ label.edit_jabaws_url = Edit JABAWS URL label.add_jabaws_url = Add new JABAWS URL label.news_from_jalview = News from http://www.jalview.org label.cut_paste_alignmen_file = Cut & Paste Alignment File -label.enter_redundancy_thereshold = Enter the redundancy threshold -label.select_dark_light_set_thereshold = Select a dark and light text colour, then set the threshold to
        switch between colours, based on background colour
        +label.enter_redundancy_threshold = Enter the redundancy threshold +label.select_dark_light_set_threshold = Select a dark and light text colour, then set the threshold to
        switch between colours, based on background colour
        label.select_feature_colour = Select Feature Colour label.delete_all = Delete all sequences warn.delete_all = Deleting all sequences will close the alignment window.
        Confirm deletion or Cancel. @@ -1221,28 +1180,23 @@ label.no_colour_selection_in_scheme = Please make a colour selection before appl label.no_colour_selection_warn = Error saving colour scheme label.open_split_window? = Would you like to open as a split window, with cDNA and protein linked? label.open_split_window = Open split window -label.no_mappings = No mappings found action.no = No action.yes = Yes label.for = for label.select_by_annotation = Select/Hide Columns by Annotation action.select_by_annotation = Select/Hide Columns by Annotation... label.threshold_filter = Threshold Filter -action.hide = Hide -action.select = Select label.alpha_helix = Alpha Helix label.beta_strand = Beta Strand label.turn = Turn label.select_all = Select All label.structures_filter = Structures Filter label.search_filter = Search Filter -label.description = Description label.include_description= Include Description action.back = Back 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_fetcher = PDB Sequence Fetcher label.result = result label.results = results @@ -1288,19 +1242,27 @@ exception.fts_server_unreachable = Jalview is unable to reach the {0} server. \n label.nw_mapping = Needleman & Wunsch Alignment label.sifts_mapping = SIFTs Mapping label.mapping_method = Sequence \u27f7 Structure mapping method -label.mapping_method = Sequence \u27f7 Structure mapping method -status.waiting_for_user_to_select_output_file = Waiting for user to select {0} file. -status.cancelled_image_export_operation = Cancelled {0} export operation. -info.error_creating_file = Error creating {0} file. +status.waiting_for_user_to_select_output_file = Waiting for user to select {0} file +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 +label.next_page_tooltip=Next Page +label.prev_page_tooltip=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. +status.launching_3d_structure_viewer = Launching 3D Structure viewer... +status.fetching_3d_structures_for_selected_entries = Fetching 3D Structures for selected entries... +status.fetching_dbrefs_for_sequences_without_valid_refs = Fetching db refs for {0} sequence(s) without valid db ref required for SIFTS mapping +status.fetching_3d_structures_for = Fetching 3D Structure for {0} +status.obtaining_mapping_with_sifts = Obtaining mapping with SIFTS +status.obtaining_mapping_with_nw_alignment = Obtaining mapping with NW alignment +status.exporting_alignment_as_x_file = Exporting alignment as {0} file +label.column = Column +label.cant_map_cds = Unable to map CDS to protein\nCDS missing or incomplete +label.operation_failed = Operation failed diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 8943860..87538be 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -38,7 +38,6 @@ action.cancel = Cancelar action.create = Crear action.update = Actualizar action.delete = Borrar -action.snapshot = Imagen action.clear = Limpiar action.accept = Aceptar action.select_ddbb = --- Seleccionar base de datos --- @@ -118,7 +117,6 @@ action.save_as_default = Guardar como por defecto action.save_as = Guardar como action.save = Guardar action.cancel_fetch = Cancelar búsqueda -action.save_omit_hidden_columns = Guardar / Omitir las columnas ocultas action.change_font = Cambiar Fuente action.change_font_tree_panel = Cambiar fuente (panel del árbol) action.colour = Color @@ -134,21 +132,22 @@ action.show_group = Mostrar grupo action.fetch_db_references = Recuperar referencias a base de datos action.view_flanking_regions = Mostrar flancos label.view_flanking_regions = Mostrar los datos de la secuencia a ambos lados de las subsecuencias implicadas en este alineamiento -label.str = Str: -label.seq = Seq: label.structures_manager = Administrar estructuras label.nickname = Sobrenombre: label.url = URL: label.input_file_url = Introducir URL en el fichero de entrada -label.select_feature = Seleccionar función: -label.name = Nombre: +label.select_feature = Seleccionar característica +label.name = Nombre +label.name\: = Nombre: label.name_param = Nombre: {0} -label.group = Grupo: +label.group = Grupo +label.group\: = Grupo: label.group_name = Nombre del grupo label.group_description = Descripción del grupo label.edit_group_name_description = Editar nombre/descripción del grupo label.colour = Color: -label.description = Descripción: +label.description = Descripción +label.description\: = Descripción: label.start = Comenzar: label.end = Terminar: label.current_parameter_set_name = Nombre actual del conjunto de parámetros: @@ -208,8 +207,8 @@ label.nucleotide = Nucle label.to_new_alignment = A nuevo alineamiento label.to_this_alignment = Añadir a este alineamiento label.apply_colour_to_all_groups = Aplicar color a todos los grupos -label.modify_identity_thereshold = Modificar el umbral de identidad... -label.modify_conservation_thereshold = Modificar el umbral de conservación... +label.modify_identity_threshold = Modificar el umbral de identidad... +label.modify_conservation_threshold = Modificar el umbral de conservación... label.input_from_textbox = Introducir desde el cuadro de texto label.centre_column_labels = Centrar las etiquetas de las columnas label.automatic_scrolling = Desplazamiento automático @@ -217,7 +216,6 @@ label.documentation = Documentaci label.about = Acerca de... label.show_sequence_limits = Mostrar los límites de la secuencia label.feature_settings = Ajustar funciones... -label.sequence_features = Funciones de la secuencia label.all_columns = Todas las columnas label.all_sequences = Todas las secuencias label.selected_columns = Columnas seleccionadas @@ -277,7 +275,7 @@ label.order_by_params = Ordenar por {0} label.html_content = {0} label.paste_pdb_file= Pegar tu fichero PDB aquí. label.paste_pdb_file_for_sequence = Pegar fichero PDB para la secuencia {0} -label.could_not_parse_newick_file = No se pudo analizar el fichero Newick\\\!\\n {0} +label.could_not_parse_newick_file = No se pudo analizar el fichero Newick\!\n {0} label.successfully_pasted_tcoffee_scores_to_alignment= Pegada exitosamente la puntuación T-Coffee al alineamiento. label.failed_add_tcoffee_scores = Fallo al añadir las puntuaciones T-Coffee: label.successfully_pasted_annotation_to_alignment = Anotación pegada exitosamente al alineamiento. @@ -291,7 +289,6 @@ label.found_match_for = Buscar coincidencia para {0} label.font = Fuente: label.size = Talla: label.style = Estilo: -label.enter_redundancy_threshold = Introducir el umbral de redundancia label.calculating = Calculando.... label.modify_conservation_visibility = Modificar la visibilidad de conservación label.colour_residues_above_occurence = Residuos de color por encima del % de aparición @@ -315,13 +312,11 @@ label.blog_item_published_on_date = {0} {1} label.select_das_service_from_table = Seleccionar servicio DAS de la tabla para leer una descripción completa aquí. label.session_update = Actualizar sesión label.new_vamsas_session = Nueva sesión Vamsas -label.load_vamsas_session = Cargar sesión Vamsas -label.save_vamsas_session = Guardar sesión Vamsas action.save_vamsas_session = Guardar Sesión Vamsas 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 @@ -336,39 +331,38 @@ label.example = Ejemplo label.example_param = Ejemplo: {0} label.select_file_format_before_saving = Debe seleccionar un formato de fichero antes de guardar! label.file_format_not_specified = Formato de fichero no especificado -label.alignment_contains_hidden_columns = El alineamiento contiene columnas ocultas.\\nQuieres guardar s\u00F3lo el alineamiento visible? label.couldnt_save_file = No se pudo guardar el fichero: {0} label.error_saving_file = Error guardando el fichero label.remove_from_default_list = eliminar de la lista de defectuosos? label.remove_user_defined_colour = Eliminar el color definido por el usuario label.you_must_select_least_two_sequences = Debes seleccionar al menos 2 secuencias. label.invalid_selection = Selección inválida -label.principal_component_analysis_must_take_least_four_input_sequences = El an\u00E1lisis de la componente principal debe tomar\\nal menos 4 secuencias de entrada. +label.principal_component_analysis_must_take_least_four_input_sequences = El an\u00E1lisis de la componente principal debe tomar\nal menos 4 secuencias de entrada. label.sequence_selection_insufficient = Selección de secuencias insuficiente label.you_need_more_two_sequences_selected_build_tree = necesitas seleccionar más de dos secuencias para construir un árbol! label.not_enough_sequences = No suficientes secuencias -label.selected_region_to_tree_may_only_contain_residues_or_gaps = La regi\u00F3n seleccionada para construir un \u00E1rbol puede\\ncontener s\u00F3lo residuos o espacios.\\nPrueba usando la funci\u00F3n Pad en el men\u00FA de edici\u00F3n,\\n o uno de los m\u00FAltiples servicios web de alineamiento de secuencias. +label.selected_region_to_tree_may_only_contain_residues_or_gaps = La regi\u00F3n seleccionada para construir un \u00E1rbol puede\ncontener s\u00F3lo residuos o espacios.\nPrueba usando la funci\u00F3n Pad en el men\u00FA de edici\u00F3n,\n o uno de los m\u00FAltiples servicios web de alineamiento de secuencias. label.sequences_selection_not_aligned = Las secuencias seleccionadas no están alineadas -label.sequences_must_be_aligned_before_creating_tree = Las secuencias deben estar alineadas antes de crear el \u00E1rbol.\\nPrueba usando la funci\u00F3n Pad en el men\u00FA de editar,\\n o uno de los m\u00FAltiples servicios web de alineamiento de secuencias. +label.sequences_must_be_aligned_before_creating_tree = Las secuencias deben estar alineadas antes de crear el \u00E1rbol.\nPrueba usando la funci\u00F3n Pad en el men\u00FA de editar,\n o uno de los m\u00FAltiples servicios web de alineamiento de secuencias. label.sequences_not_aligned = Secuencias no alineadas label.problem_reading_tree_file = Problema al leer el fichero del árbol label.possible_problem_with_tree_file = Posible problema con el fichero del árbol label.select_at_least_three_bases_in_at_least_one_sequence_to_cDNA_translation = Por favor seleccionar al menos tres bases de al menos una secuencia para poder realizar la traducción de cDNA. label.translation_failed = Translation Failed -label.error_when_translating_sequences_submit_bug_report = Desafortunadamente, algo fue mal a la hora de traducir tus secuencias.\\nPor favor, revisa la consola Jalview java \\ny presenta un informe de error que incluya el seguimiento. +label.error_when_translating_sequences_submit_bug_report = Desafortunadamente, algo fue mal a la hora de traducir tus secuencias.\nPor favor, revisa la consola Jalview java \ny presenta un informe de error que incluya el seguimiento. label.implementation_error = Error de implementación: label.automatically_associate_pdb_files_with_sequences_same_name = Quieres asociar automáticamente los {0} ficheros PDB con las secuencias del alineamiento que tengan el mismo nombre? label.automatically_associate_pdb_files_by_name = Asociar los ficheros PDB por nombre automáticamente label.ignore_unmatched_dropped_files_info = Quieres ignorar los {0} ficheros cuyos nombres no coincidan con ningún IDs de las secuencias ? label.ignore_unmatched_dropped_files = Ignorar los ficheros sin coincidencias? -label.enter_view_name = Introducir nombre visible (¿?) +label.enter_view_name = Introduzca un nombre para la vista label.enter_label = Introducir etiqueta -label.enter_label_for_the_structure = Introducir una etiqueta para la estructura? -label.pdb_entry_is_already_displayed = {0} Ya est\u00E1 mostrado.\\nQuieres volver a usar este visor? +label.enter_label_for_the_structure = Introducir una etiqueta para la estructura +label.pdb_entry_is_already_displayed = {0} Ya est\u00E1 mostrado.\nQuieres volver a usar este visor? label.map_sequences_to_visible_window = Mapa de secuencias en ventana visible: {0} -label.add_pdbentry_to_view = Quieres a\u00F1adir {0} a la vista llamada\\n{1}\\n +label.add_pdbentry_to_view = Quieres a\u00F1adir {0} a la vista llamada\n{1}\n label.align_to_existing_structure_view = Alinear a una estructura ya existente -label.pdb_entries_couldnt_be_retrieved = Las siguientes entradas pdb no pueden ser extra\u00EDdas del PDB\\\:\\n{0}\\nPor favor, prueba descarg\u00E1ndolas manualmente. +label.pdb_entries_couldnt_be_retrieved = Las siguientes entradas pdb no pueden ser extra\u00EDdas del PDB\:\n{0}\nPor favor, prueba descarg\u00E1ndolas manualmente. label.couldnt_load_file = No se pudo cargar el fichero label.couldnt_find_pdb_id_in_file = No se pudo encontrar un Id PDB en el fichero suministrado. Por favor, introduzca un Id para identificar esta estructura. label.no_pdb_id_in_file = No hay un Id PDB en el fichero @@ -394,11 +388,11 @@ label.invalid_url = URL Invalido! label.error_loading_file = Error al cargar el fichero label.problems_opening_file = Encontrados problemas al abrir el fichero {0}!! label.file_open_error = Error al abrir el fichero -label.no_das_sources_selected_warn = No han sido seleccionadas fuentes DAS.\\nPor favor, seleccione algunas fuentes y\\npruebe de nuevo. +label.no_das_sources_selected_warn = No han sido seleccionadas fuentes DAS.\nPor favor, seleccione algunas fuentes y\npruebe de nuevo. label.no_das_sources_selected_title = No han sido seleccionadas fuentes DAS -label.colour_scheme_exists_overwrite = El esquema de colores {0} ya existe.\\nContinuar guardando el esquema de colores como {1}? +label.colour_scheme_exists_overwrite = El esquema de colores {0} ya existe.\nContinuar guardando el esquema de colores como {1}? label.duplicate_scheme_name = Duplicar nombre de esquema -label.jalview_new_questionnaire = Hay un nuevo cuestionario disponible. Querr\u00EDa completarlo ahora ?\\n +label.jalview_new_questionnaire = Hay un nuevo cuestionario disponible. Querr\u00EDa completarlo ahora ?\n label.jalview_user_survey = Encuesta de usuario Jalview label.alignment_properties = Propiedades del alineamiento: {0} label.alignment_props = Propiedades del alineamiento @@ -436,7 +430,6 @@ label.no_features_added_to_this_alignment = No hay funciones asociadas a este al label.features_can_be_added_from_searches_1 = (Las funciones pueden ser añadidas de búsquedas o label.features_can_be_added_from_searches_2 = de ficheros de funciones Jalview / GFF) label.calculating_pca= Calculando PCA -label.reveal_columns = Mostrar Columnas label.jalview_cannot_open_file = Jalview no puede abrir el fichero label.jalview_applet = Aplicación Jalview label.loading_data = Cargando datos @@ -444,7 +437,6 @@ label.memory_stats = Memoria libre total: {0} MB; Memoria m label.calculating_tree = Calculando árbol label.state_queueing = En cola label.state_running = Procesando -label.state_complete = Completar label.state_completed = Finalizado label.state_job_cancelled = ¡Trabajo cancelado! label.state_job_error = Error del trabajo! @@ -458,8 +450,6 @@ label.load_associated_tree = Cargar label.load_features_annotations = Cargar características/anotaciones ... label.export_features = Exportar características... label.export_annotations = Exportar anotaciones ... -label.jalview_copy = Copiar (sólo Jalview) -label.jalview_cut = Cortar (sólo Jalview) label.to_upper_case = Pasar a mayúsculas label.to_lower_case = Pasar a minúsculas label.toggle_case = Alternar mayúsculas y minúsculas @@ -468,8 +458,9 @@ label.create_sequence_feature = Crear funci label.edit_sequence = Editar secuencia label.edit_sequences = Editar secuencias label.sequence_details = Detalles de la secuencia -label.jmol_help = Ayuda de Jmol -label.all = Todo +label.jmol_help = Ayuda de Jmol +# Todos/Todas is gender-sensitive, but currently only used for feminine (cadena / anotación)! +label.all = Todas label.sort_by = Ordenar por label.sort_by_score = Ordenar por puntuación label.sort_by_density = Ordenar por densidad @@ -485,15 +476,14 @@ label.reset_min_max_colours_to_defaults = Reiniciar los colores min y max colour label.align_structures_using_linked_alignment_views = Alinear las estructuras utlizando las {0} vistas de alineamiento enlazadas label.connect_to_session = Conectar a la sesión {0} label.threshold_feature_display_by_score = Filtrar la característica mostrada por puntuación. -label.threshold_feature_no_thereshold = Sin umbral -label.threshold_feature_above_thereshold = Por encima del umbral -label.threshold_feature_below_thereshold = Por debajo del umbral -label.adjust_thereshold = Ajustar umbral +label.threshold_feature_no_threshold = Sin umbral +label.threshold_feature_above_threshold = Por encima del umbral +label.threshold_feature_below_threshold = Por debajo del umbral +label.adjust_threshold = Ajustar umbral label.toggle_absolute_relative_display_threshold = Cambiar entre mostrar el umbral absoluto y el relativo. label.display_features_same_type_different_label_using_different_colour = Mostrar las características del mismo tipo con una etiqueta diferente y empleando un color distinto (p.e. características del dominio) label.select_colour_minimum_value = Seleccionar el color para el valor mínimo label.select_colour_maximum_value = Seleccionar el color para el valor máximo -label.open_new_jmol_view_with_all_structures_associated_current_selection_superimpose_using_alignment = Abrir una nueva vista Jmol con todas las estructuras asociadas con la selección acxtual y superponer las utilizando el alineamiento. label.open_url_param = Abrir URL {0} label.open_url_seqs_param = Abrir URL ({0}..) ({1} secuencias) label.load_pdb_file_associate_with_sequence = Cargar un fichero PDB y asociarlo con la secuencia {0} @@ -544,10 +534,9 @@ label.histogram = Histograma label.logo = Logo label.non_positional_features = Características no posicionales label.database_references = Referencias a base de datos -label.share_selection_across_views = Compartir la selección en todas las vistas -label.scroll_highlighted_regions = Desplazarse hasta las regiones resaltadas +#label.share_selection_across_views = Compartir la selección en todas las vistas +#label.scroll_highlighted_regions = Desplazarse hasta las regiones resaltadas label.gap_symbol = Símbolo del hueco -label.alignment_colour = Color del alineamiento label.address = Dirección label.port = Puerto label.default_browser_unix = Navegador por defecto (Unix) @@ -626,18 +615,11 @@ label.cut_paste = Cortar y pegar label.adjusting_parameters_for_calculation = Ajustar los parámetros para el cálculo existente label.2d_rna_structure_line = 2D RNA {0} label.2d_rna_sequence_name = 2D RNA - {0} -label.edit_name_and_description_current_group = Editar el nombre y la descripción del grupo actual. -label.view_structure_for = Visualizar la estructura para {0} -label.view_all_structures = Visualizar todas las {0} estructuras. -label.view_all_representative_structures = Visualizar todas las {0} estructuras representativas. -label.open_new_jmol_view_with_all_representative_structures_associated_current_selection_superimpose_using_alignment = Abrir una nueva vista de Jmol con todas las estructuras representativas\nasociadas con la selecci\u00F3n actual\nsuperpuesta con el alineamiento actual. -label.associate_structure_with_sequence = Asociar estructura con la secuencia +label.edit_name_and_description_current_group = Editar el nombre y la descripción del grupo actual label.from_file = desde fichero label.enter_pdb_id = Introducir PDB Id -label.discover_pdb_ids = Buscar PDB ids label.text_colour = Color del texto label.structure = Estructura -label.view_structure = Visualizar estructura label.clustalx_colours = Colores de Clustalx label.above_identity_percentage = Sobre % identidad label.create_sequence_details_report_annotation_for = Anotación para {0} @@ -670,7 +652,6 @@ label.use_registry = Utilizar el registro label.add_local_source = Añadir fuente local label.set_as_default = Establecer por defecto label.show_labels = Mostrar etiquetas -label.background_colour = Color de fondo label.associate_nodes_with = Asociar nodos con label.jalview_pca_calculation = Cálculo del PCA por Jalview label.link_name = Nombre del enalce @@ -763,17 +744,10 @@ label.user_preset = Preselecci label.service_preset = Preselección del servicio label.run_with_preset = Ejecutar {0} con preselección label.view_service_doc_url = Visualizar {1} -label.submit_sequence = Enviar {0} {1} {2} {3} a
        {4} action.by_title_param = por {0} -label.alignment = Alineamiento -label.secondary_structure_prediction = Predicción de la estructura secundaria -label.sequence_database_search = Búsqueda en base de datos de secuencias -label.analysis = Análisis -label.protein_disorder = Desorden en la proteína label.source_from_db_source = Fuentes de {0} label.from_msname = de {0} label.superpose_with = Superponer con... -action.do = Hacer label.scale_label_to_column = Ajustar la etiqueta a la columna label.add_new_row = Añadir nuevo fila label.edit_label_description = Editar etiqueta/descripción @@ -817,7 +791,7 @@ label.service_url = URL del servicio label.copied_sequences = Secuencias copiadas label.cut_sequences = Cortar secuencias label.conservation_colour_increment = Incremento de Conservación del Color ({0}) -label.percentage_identity_thereshold = Umbral del Porcentaje de Identidad ({0}) +label.percentage_identity_threshold = Umbral del Porcentaje de Identidad ({0}) label.error_unsupported_owwner_user_colour_scheme = Propietario no soportado para el diálogo del Esquema Cromático del Usuario label.save_alignment_to_file = Guardar Alineamiento en fichero label.save_features_to_file = Guardar Características en un fichero @@ -833,7 +807,6 @@ label.save_vamsas_document_archive = Guardar el archivo de documento Vamsas label.saving_vamsas_doc = Guardando el documento VAMSAS en {0} label.load_feature_colours = Cargar colores de características label.save_feature_colours = Guardar esquema cromático de características -label.dataset_for = {0} conjunto de datos para {1} label.select_startup_file = Seleccionar fichero de arranque label.select_default_browser = Seleccionar navegador web por defecto label.save_tree_as_newick = Guardar árbol como fichero newick @@ -861,15 +834,9 @@ error.null_from_clone1 = Nulo de clone1! error.implementation_error_sortbyfeature = Error de implementación - sortByFeature debe ser uno de FEATURE_SCORE, FEATURE_LABEL o FEATURE_DENSITY. error.not_yet_implemented = No se ha implementado todavía error.unknown_type_dna_or_pep = Tipo desconocido {0} - dna o pep son los únicos valores permitidos -error.implementation_error_dont_know_thereshold_annotationcolourgradient = Error de implementación: no se conoce el valor umbral para el AnnotationColourGradient actual. -error.implementation_error_embeddedpopup_not_null = Error de implementación - embeddedPopup debe ser no nulo. -error.invalid_colour_for_mycheckbox = Color no válido para MyCheckBox -error.implementation_error_unrecognised_render_object_for_features_type = Error de implementación: no se reconoce el objeto de representación {0} para las características de tipo {1} -error.implementation_error_unsupported_feature_colour_object = Error de implementación: objeto de color de características no soportado. +error.implementation_error_dont_know_threshold_annotationcolourgradient = Error de implementación: no se conoce el valor umbral para el AnnotationColourGradient actual. error.invalid_separator_parameter = Separador de parámetros no válido - debe tener longitud mayor que cero error.alignment_cigararray_not_implemented = Alignment(CigarArray) no se ha implementado todavía -error.weak_sequencei_equivalence_not_yet_implemented = Equivalencia débil sequenceI no se ha implementado todavía. -error.implementation_error_can_only_make_alignmnet_from_cigararray = Error de implementación - sólo se puede construir un vista de alineamiento a partir de una CigarArray de secuencias. error.empty_view_cannot_be_updated = una vista vacía no se puede actualizar. error.mismatch_between_number_of_sequences_in_block = No hay coincidencia entre el número de secuencias en el bloque {0} ({1}) y la vista original ({2}) error.padding_not_yet_implemented = El relleno no se ha implementado todavía @@ -891,21 +858,18 @@ error.not_yet_implemented_cigar_object_from_cigar_string = No implementado todav error.implementation_bug_cigar_operation = Bug de implementación. La operación Cigar {0} {1} no es ni {2}, ni {3} ni {4}. error.implementation_error_for_new_cigar = Error de implementación en new Cigar(SequenceI) error.implementation_error_cigar_seq_no_operations = Error de implementación: la {0}a secuencia Cigar no tiene operaciones. -error.implementation_error_jmol_getting_data = Error de implementación - Jmol parece estar todavía intentando recuperar sus datos - informe de ello en http://issues.jalview.org/browse/JAL-1016 error.implementation_error_no_pdbentry_from_index = Error de implementación - no existe la correspondiente entrada pdb (para el índice {0}) para añadir el mapeo de secuencias a error.jmol_version_not_compatible_with_jalview_version = La versión {0} de Jmol no es compatible con esta versión de Jalview. Informe de este problema en http://issues.jalview.org error.not_implemented_remove = Borrar: no implementado error.not_implemented_clone = Clonar: no implementado -error.implementation_error_chimera_getting_data = Error de implementación - Chimera parece estar todavía intentando recuperar sus datos - informe de ello en http://issues.jalview.org/browse/JAL-1016 error.call_setprogressbar_before_registering_handler = llamada a setProgressBar antes de registrar el manejador de la barra de estado label.cancelled_params = {0} cancelado error.implementation_error_cannot_show_view_alignment_frame = Error de implementación: no es posible mostrar una vista de otro alineamiento en un AlignFrame. -error.implementation_error_dont_know_about_thereshold_setting = Error de implementación: no se conoce la configuración del umbral para el AnnotationColourGradient actual. +error.implementation_error_dont_know_about_threshold_setting = Error de implementación: no se conoce la configuración del umbral para el AnnotationColourGradient actual. error.eps_generation_not_implemented = La generación de EPS no se ha implementado todavía error.png_generation_not_implemented = La generación de PNG no se ha implementado todavía error.try_join_vamsas_session_another = Tratando de establecer una sesión VAMSAS cuando ya había otra conectada error.invalid_vamsas_session_id = Identificador de sesión VAMSAS no válido -error.implementation_error_cannot_create_groovyshell = Error de implementación:no se puede crear groovyShell sin Groovy en el classpath label.groovy_support_failed = El soporte Groovy de Jalview ha fallado label.couldnt_create_groovy_shell = No es posible crear el shell de Groovy. Compruebe el fichero de log para conocer los detalles. error.unsupported_version_calcIdparam = Versión no soportada de {0} @@ -920,7 +884,6 @@ error.setstatus_called_non_existent_job_pane = se lllamado a setStatus para el p error.implementation_error_cannot_find_marshaller_for_param_set =Error de implementación: no puede encontrar un marshaller para el conjunto de parámetros error.implementation_error_old_jalview_object_not_bound =Error de implementación: ¡el objeto Jalview antiguo no está enlazado! ({0}) error.implementation_error_vamsas_doc_class_should_bind_to_type = Error de implementación: la clase de documento VAMSAS {0} debe enlazar a {1} (pero se ha encontrado que lo está a {2}) -error.implementation_error_jalview_class_should_bind_to_type = Error de implementación: la clase Jalview {0} debe enlazar a {1} (pero se ha encontrado que lo está a {2}) error.invalid_vamsas_rangetype_cannot_resolve_lists = RangeType VAMSAS no válido - ¡no es posible resolver ambas listas de Pos y Seg con los valores elegidos! error.implementation_error_maplist_is_null = Error de implementación. MapList es nulo en initMapType. error.implementation_error_cannot_have_null_alignment = Error de implementación: no es posible tener una clave nula en el alineamiento @@ -962,10 +925,11 @@ 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 conteniendo características del tipo {2} en {3} secuencia(s) +label.view_controller_toggled_marked = {0} {1} columnas {2} características del tipo {3} en {4} secuencia(s) label.toggled = Invertida label.marked = Marcada -label.not = no +label.containing = conteniendo +label.not_containing = no conteniendo label.no_feature_of_type_found = No se han encontrado características del tipo {0}. label.submission_params = Envío {0} label.empty_alignment_job = Trabajo de alineamiento vacío @@ -975,7 +939,7 @@ label.pca_recalculating = Recalculando PCA label.pca_calculating = Calculando PCA label.select_foreground_colour = Escoger color del primer plano label.select_colour_for_text = Seleccione el color del texto -label.adjunst_foreground_text_colour_thereshold = Ajustar el umbral del color del texto en primer plano +label.adjunst_foreground_text_colour_threshold = Ajustar el umbral del color del texto en primer plano label.select_subtree_colour = Seleccioanr el color del sub-árbol label.create_new_sequence_features = Crear nueva(s) característica(s) de secuencia label.amend_delete_features = Arrelgar/Borrar características de {0} @@ -995,7 +959,6 @@ exception.mismatched_unseen_closing_char = Discordancia (no vista) en el car exception.mismatched_closing_char = Carácter de cierre discordante {0} exception.mismatched_opening_char = Carácter de apertura discordante {0} en {1} exception.invalid_datasource_couldnt_obtain_reader = Fuente de datos no válida. No es posible obtener el Reader -exception.index_value_not_in_range = {0}: el valor del índice {1} en se encuentra en el rango [0..{2}] exception.unterminated_cigar_string = Cadena cigar sin terminar exception.unexpected_operation_cigar_string_pos = Operación no esperada {0} en una cadena cigar (posición {1} en {2}) exception.couldnt_parse_responde_from_annotated3d_server = No es posible parsear la respuesta procedente del servidor Annotate3d @@ -1023,7 +986,6 @@ exception.ranml_problem_parsing_data = Problema parseando los datos como RNAML ( exception.pfam_no_sequences_found = No se han encontrado secuencias (entrada PFAM) exception.stockholm_invalid_format = Este fichero no es tiene un formato STOCKHOLM válido: la primera línea no contiene '# STOCKHOLM' exception.couldnt_parse_sequence_line = No es posible parse la línea de secuencia: {0} -exception.error_parsing_line = Error parseando {0} exception.unknown_annotation_detected = Anotación desconocida detectada: {0} {1} exception.couldnt_store_sequence_mappings = No es posible almacenar los mapeos de secuencia para {0} exception.matrix_too_many_iteration = Demasiadas iteraciones en {0} (el máximo es {1}) @@ -1031,7 +993,6 @@ exception.browser_not_found = Excepci exception.browser_unable_to_locate = Imposible encontrar el navegador: {0} exception.invocation_target_exception_creating_aedesc = InvocationTargetException mientras se creaba AEDesc: {0} exception.illegal_access_building_apple_evt= IllegalAccessException mientras se construía AppleEvent: {0} -exception.instantiation_creating_aedesc = InstantiationException mientras se creaba AEDesc: {0} exception.unable_to_launch_url = Imposible lanzar la URL: {0} exception.unable_to_create_internet_config = Imposible crear una instancia de configuración de Internet: {0} exception.invocation_target_calling_url = InvocationTargetException mientras se invocaba openURL: {0} @@ -1040,8 +1001,6 @@ exception.interrupted_launching_browser = InterruptedException mientras se lanza exception.das_source_doesnt_support_sequence_command = La fuente {0} no soporta el comando sequence. exception.invalid_das_source = Fuente DAS no válida: {0} exception.ebiembl_retrieval_failed_on = La recuperación de datos EBI EMBL XML ha fallado en {0}:{1} -label.no_embl_record_found = # No se ha recuperado ningún registro EMBL de {0}:{1} -label.embl_successfully_parsed = # Se han parseado con éxito las consultas {0} en un alineamiento exception.no_pdb_records_for_chain = No se han encontrado registros {0} para la cadena {1} exception.unexpected_handling_rnaml_translation_for_pdb = Excepcion inesperada cuando se traducían a RNAML los datos PDB exception.couldnt_recover_sequence_properties_for_alignment = No es posible recuperar las propiedades de la secuencia para el alineamiento @@ -1055,7 +1014,7 @@ error.implementation_error_cannot_find_service_url_in_given_set = Error de imple error.implementation_error_cannot_find_service_url_in_given_set_param_store = Error de implementación: la URL del servicio en el conjunto de URL para este almacén de parámetros del servicio({0}) exception.jobsubmission_invalid_params_set = Conjunto de parámetros no válido. Comprueba la implementación de Jalview exception.notvaliddata_group_contains_less_than_min_seqs = El grupo contiene menos de {0} secuencias. -exception.outofmemory_loading_pdb_file = Sin menoria al cargar el fichero PDB +exception.outofmemory_loading_pdb_file = Sin memoria al cargar el fichero PDB exception.eps_coudnt_write_output_file = No es posible escribir el fichero de salida: {0} exception.eps_method_not_supported = Método actualmente no suportado por la versión {0} de EpsGraphics2D exception.eps_unable_to_get_inverse_matrix = Imposible obtener la inversa de la matrix: {0} @@ -1084,7 +1043,7 @@ status.fetching_pdb = Recuperando PDB {0} status.refreshing_news = Refrescando noticias status.importing_vamsas_session_from = Importando sesión VAMSAS de {0} status.opening_params = Abriendo {0} -status.waiting_sequence_database_fetchers_init = Esperando la inicialización de los recuperadores de bases de datos de secuencias +status.waiting_sequence_database_fetchers_init = Esperando inicialización de los recuperadores de bases de datos de secuencias status.init_sequence_database_fetchers = Inicializando recuperadores de bases de datos de secuencias status.fetching_sequence_queries_from = Recuperando {0} consultas de secuencias de {1} status.finshed_querying = Consulta finalizada @@ -1123,8 +1082,8 @@ label.edit_jabaws_url = Editar JABAWS URL label.add_jabaws_url = Añadir nueva JABAWS URL label.news_from_jalview = Noticias de http://www.jalview.org label.cut_paste_alignmen_file = Cortar & Pegar fichero de alineamiento -label.enter_redundancy_thereshold = Introducir el umbral de redundancia -label.select_dark_light_set_thereshold = Seleccionar un color oscuro y un color claro para el texto y establecer el umbral en que
        cambiar entre colores, basándose en el color de fondo
        +label.enter_redundancy_threshold = Introducir el umbral de redundancia +label.select_dark_light_set_threshold = Seleccionar un color oscuro y un color claro para el texto y establecer el umbral en que
        cambiar entre colores, basándose en el color de fondo
        label.select_feature_colour = Seleccionar color de las características label.ignore_gaps_consensus = Ignorar huecos en el consenso label.show_group_histogram = Mostrar histograma de grupo @@ -1144,7 +1103,6 @@ action.feature_settings=Ajustes de caracter info.invalid_msa_notenough=No suficientes datos de sequencia para alinear label.result=resultado label.results=resultados -label.no_mappings=No hay mapeados encontrados label.struct_from_pdb=Procesar la estructura secundaria PDB label.hide_selected_annotations=Ocultar anotaciones seleccionados info.select_annotation_row=Seleccionar Fila de Anotaciones @@ -1168,7 +1126,6 @@ action.no=No action.yes=Sí label.export_settings=Exportar Ajustes label.linked_view_title=Vista vinculada de cDNA y proteína -label.view_protein_structure=Ver Estructura Proteica label.couldnt_read_data=No se pudo leer los datos status.opening_file=abriendo fichero label.except_selected_sequences=Todo excepto secuencias seleccionadas @@ -1213,7 +1170,6 @@ action.select_by_annotation=Seleccionar/Ocultar Columnas por Anotaci action.export_features=Exportar Características error.invalid_regex=Expresión regular inválida label.autoadd_temp=Añadir anotación factor de temperatura al alineamiento -tooltip.rnalifold_settings=Modificar la configuración de la predicción RNAAlifold. Úselo para ocultar o mostrar resultados del cálculo de RNA, o cambiar parámetros de el plegado de RNA. label.chimera_path_tip=Jalview intentará primero las rutas introducidas aquí, Y si no las rutas usuales de instalación label.structure_chooser=Selector de Estructuras label.structure_chooser_manual_association=Selector de Estructuras - asociación manual @@ -1229,9 +1185,7 @@ label.colour_with_chimera=Colorear con Chimera label.show_pdbstruct_dialog=Datos de Estructura 3D... label.hide_all=Ocultar todos label.invert=Invertir -label.pdb_sequence_getcher=Buscador de Secuencias PDB tooltip.aacon_settings=Cambiar ajustes para cálculos AACon -label.align=Alinear label.mark_as_representative=Marcar como representativa label.include_description=Incluir Descripción label.for=para @@ -1247,15 +1201,13 @@ info.associate_wit_sequence=Asociar con secuencia label.protein=Proteína warn.oneseq_msainput_selection=La selección actual sólo contiene una única secuencia. ¿Quieres enviar todas las secuencias para la alineación en su lugar? label.use_rnaview=Usar RNAView para estructura secondaria -label.opens_the_jabaws_server_homepage=Abre la página de inicio del servidor JABAWS en navegador label.search_all=Introducir uno o más valores de búsqueda separados por punto y coma ";" (Nota: buscará en toda la base de datos PDB) label.confirm_close_chimera=Cerrará la conexión de Jalview a {0}.
        ¿Quieres cerrar la ventana Chimera también? tooltip.rnalifold_calculations=Se calcularán predicciones de estructura secondaria de RNA para el alineaminento, y se actualizarán si se efectuan cambios -Calcular predicciónes de estructura secondaria para el alineamiento +tooltip.rnalifold_settings=Modificar la configuración de la predicción RNAAlifold. Úselo para ocultar o mostrar resultados del cálculo de RNA, o cambiar parámetros de el plegado de RNA. label.show_selected_annotations=Mostrar anotaciones seleccionadas status.colouring_chimera=Coloreando Chimera label.configure_displayed_columns=Configurar Columnas Mostradas -exception.pdb_server_error=Error desde el servidor PDB exception.resource_not_be_found=El recurso solicitado no se ha encontrado label.aacon_calculations=cálculos AACon label.pdb_web-service_error=Error de servicio web PDB @@ -1264,10 +1216,8 @@ label.chimera_path=Ruta de acceso a programa Chimera warn.delete_all=Borrar todas las secuencias cerrará la ventana del alineamiento.
        Confirmar o Cancelar. label.select_all=Seleccionar Todos label.alpha_helix=Hélice Alfa -label.sequence_details_for=Detalles de secuencia para {0} label.chimera_help=Ayuda para Chimera label.find_tip=Buscar alineamiento, selección o IDs de secuencia para una subsecuencia (sin huecos) -exception.pdb_server_unreachable=Jalview no puede conectar con el servidor PDBE Solr.\nPor favor, asegúrese de que está conectado a Internet y vuelva a intentarlo. label.structure_viewer=Visualizador de estructura for defecto label.embbed_biojson=Incrustar BioJSON al exportar HTML label.transparency_tip=Ajustar la transparencia a "ver a través" los colores de las características. @@ -1280,4 +1230,40 @@ info.select_filter_option=Escoger Opci info.invalid_msa_input_mininfo=Necesita por lo menos dos secuencias con al menos 3 residuos cada una, sin regiones ocultas entre ellas. label.chimera_missing=Visualizador de estructura Chimera no encontrado.
        Por favor, introduzca la ruta de Chimera,
        o descargar e instalar la UCSF Chimera. label.save_as_biojs_html=Guardar como HTML BioJs -exception.pdb_rest_service_no_longer_available=Servicios Rest PDB ya no están disponibles! +exception.fts_server_unreachable=Jalview no puede conectar con el servidor {0}. \nPor favor asegúrese de que está conectado a Internet y vuelva a intentarlo. +exception.outofmemory_loading_mmcif_file=Sin memoria al cargar el fichero mmCIF +label.hide_columns_not_containing=Ocultar las columnas que no contengan +label.pdb_sequence_fetcher=Recuperador de secuencias PDB +exception.fts_server_error=Parece que hay un error desde el servidor {0} +exception.service_not_available=Servicio no disponible. El servidor se está actualizando, vuelva a intentarlo más tarde. +status.waiting_for_user_to_select_output_file=Esperando que el usuario seleccione el fichero {0} +action.prev_page=<< +status.cancelled_image_export_operation=Operación de exportación {0} cancelada +label.couldnt_run_groovy_script=No se ha podido ejecutar el script Groovy +exception.bad_request=Solicitud incorrecta. Hay un problema con su entrada. +label.run_groovy=Ejecutar script Groovy desde la consola +action.next_page=>> +label.uniprot_sequence_fetcher=Recuperador de secuencias UniProt +label.prev_page_tooltip=Página anterior +label.reverse=Invertir +label.hide_columns_containing=Ocultar las columnas que contengan +label.nucleotides=Nucleótidos +label.run_groovy_tip = Ejecutar script Groovy desde la consola sobre este alineamiento +label.nw_mapping=Alineamiento Needleman y Wunsch +label.proteins=Proteína +label.reverse_complement=Invertir y complementar +label.next_page_tooltip=Página siguiente +label.sifts_mapping=Mapeado SIFTs +label.mapping_method=Método de mapeo de secuencia \u27F7 estructura +info.error_creating_file=Error al crear fichero {0} +exception.fts_rest_service_no_longer_available= Servicios Rest {0} ya no están disponibles! +status.launching_3d_structure_viewer=Lanzando visualizador de estructura 3D... +status.obtaining_mapping_with_sifts=Obteniendo mapeo por SIFTS +status.fetching_3d_structures_for=Buscando la estructura 3D para {0} +status.fetching_3d_structures_for_selected_entries=Buscando las estructuras 3D para entradas seleccionadas... +status.fetching_dbrefs_for_sequences_without_valid_refs=Buscando referencias para {0} secuencia(s) sin referencia válida necesaria para mapeado SIFTS +status.obtaining_mapping_with_nw_alignment=Obteniendo mapeo por alineamiento Needleman y Wunsch +status.exporting_alignment_as_x_file = Exportando alineamiento como fichero tipo {0} +label.column = Columna +label.cant_map_cds = No se pudo mapear CDS a proteína\nDatos CDS faltantes o incompletos +label.operation_failed = Operación fallada diff --git a/schemas/jalview.xsd b/schemas/jalview.xsd index 850b223..f0bd638 100755 --- a/schemas/jalview.xsd +++ b/schemas/jalview.xsd @@ -293,6 +293,7 @@ + diff --git a/src/MCview/Atom.java b/src/MCview/Atom.java index 7db845b..84fefc0 100755 --- a/src/MCview/Atom.java +++ b/src/MCview/Atom.java @@ -81,7 +81,7 @@ public class Atom chain = str.substring(21, 22); resNumber = Integer.parseInt(str.substring(22, 26).trim()); - resNumIns = str.substring(22, 27); + resNumIns = str.substring(22, 27).trim(); insCode = str.substring(26, 27).charAt(0); this.x = (new Float(str.substring(30, 38).trim()).floatValue()); this.y = (new Float(str.substring(38, 46).trim()).floatValue()); @@ -109,6 +109,23 @@ public class Atom } } + @Override + public boolean equals(Object that) + { + if (this == that || that == null) + { + return true; + } + if (that instanceof Atom) + { + Atom other = (Atom) that; + return other.resName.equals(this.resName) + && other.resNumber == this.resNumber + && other.resNumIns.equals(this.resNumIns) + && other.chain.equals(this.chain); + } + return false; + } public Atom(float x, float y, float z) { this.x = x; diff --git a/src/MCview/PDBChain.java b/src/MCview/PDBChain.java index e048cd6..67ff286 100755 --- a/src/MCview/PDBChain.java +++ b/src/MCview/PDBChain.java @@ -31,8 +31,8 @@ import jalview.datamodel.SequenceI; import jalview.schemes.Colour; import jalview.schemes.ColourSchemeI; import jalview.schemes.ResidueProperties; +import jalview.structure.StructureImportSettings; import jalview.structure.StructureMapping; -import jalview.structure.StructureViewSettings; import java.awt.Color; import java.util.List; @@ -84,7 +84,7 @@ public class PDBChain public PDBChain(String pdbid, String id) { - this.pdbid = pdbid.toLowerCase(); + this.pdbid = pdbid == null ? pdbid : pdbid.toLowerCase(); this.id = id; } @@ -359,18 +359,6 @@ public class PDBChain else { - // boolean baseDetected = false; - // for (Atom resAtom : resAtoms) - // { - // if (resAtom.insCode == ' ') - // { - // baseDetected = true; - // } - // } - // if (!baseDetected) - // { - // continue; - // } // Make a new Residue object with the new atoms vector residues.addElement(new Residue(resAtoms, resNumber - 1, count)); @@ -380,7 +368,6 @@ public class PDBChain SequenceFeature sf = new SequenceFeature("RESNUM", tmpat.resName + ":" + tmpat.resNumIns + " " + pdbid + id, "", offset + count, offset + count, pdbid); - // MCview.PDBChain.PDBFILEFEATURE); resFeatures.addElement(sf); resAnnotation.addElement(new Annotation(tmpat.tfactor)); // Keep totting up the sequence @@ -438,7 +425,7 @@ public class PDBChain // System.out.println("PDB Sequence is :\nSequence = " + seq); // System.out.println("No of residues = " + residues.size()); - if (StructureViewSettings.isShowSeqFeatures()) + if (StructureImportSettings.isShowSeqFeatures()) { for (i = 0, iSize = resFeatures.size(); i < iSize; i++) { @@ -599,12 +586,13 @@ public class PDBChain { for (AlignmentAnnotation ana : sequence.getAnnotation()) { - List transfer = sq + List transfer = dsq .getAlignmentAnnotations(ana.getCalcId(), ana.label); if (transfer == null || transfer.size() == 0) { ana = new AlignmentAnnotation(ana); ana.liftOver(dsq, sqmpping); + dsq.addAlignmentAnnotation(ana); // mapping.transfer(ana); } else diff --git a/src/MCview/PDBViewer.java b/src/MCview/PDBViewer.java index e032c7a..66ce147 100755 --- a/src/MCview/PDBViewer.java +++ b/src/MCview/PDBViewer.java @@ -151,7 +151,7 @@ public class PDBViewer extends JInternalFrame implements Runnable { EBIFetchClient ebi = new EBIFetchClient(); String query = "pdb:" + pdbentry.getId(); - pdbentry.setFile(ebi.fetchDataAsFile(query, "default", "raw", ".xml") + pdbentry.setFile(ebi.fetchDataAsFile(query, "default", ".xml") .getAbsolutePath()); if (pdbentry.getFile() != null) @@ -415,34 +415,51 @@ public class PDBViewer extends JInternalFrame implements Runnable @Override public void mousePressed(MouseEvent evt) { - if (evt.isPopupTrigger()) + if (evt.isPopupTrigger()) // Mac { - radioItem.removeActionListener(radioItem.getActionListeners()[0]); - - int option = JOptionPane.showInternalConfirmDialog( - jalview.gui.Desktop.desktop, - MessageManager - .getString("label.remove_from_default_list"), - MessageManager - .getString("label.remove_user_defined_colour"), - JOptionPane.YES_NO_OPTION); - if (option == JOptionPane.YES_OPTION) - { - jalview.gui.UserDefinedColours - .removeColourFromDefaults(radioItem.getText()); - coloursMenu.remove(radioItem); - } - else + offerRemoval(radioItem); + } + } + + @Override + public void mouseReleased(MouseEvent evt) + { + if (evt.isPopupTrigger()) // Windows + { + offerRemoval(radioItem); + } + } + + /** + * @param radioItem + */ + void offerRemoval(final JRadioButtonMenuItem radioItem) + { + radioItem.removeActionListener(radioItem.getActionListeners()[0]); + + int option = JOptionPane.showInternalConfirmDialog( + jalview.gui.Desktop.desktop, + MessageManager + .getString("label.remove_from_default_list"), + MessageManager + .getString("label.remove_user_defined_colour"), + JOptionPane.YES_NO_OPTION); + if (option == JOptionPane.YES_OPTION) + { + jalview.gui.UserDefinedColours + .removeColourFromDefaults(radioItem.getText()); + coloursMenu.remove(radioItem); + } + else + { + radioItem.addActionListener(new ActionListener() { - radioItem.addActionListener(new ActionListener() + @Override + public void actionPerformed(ActionEvent evt) { - @Override - public void actionPerformed(ActionEvent evt) - { - user_actionPerformed(evt); - } - }); - } + user_actionPerformed(evt); + } + }); } } }); diff --git a/src/MCview/PDBfile.java b/src/MCview/PDBfile.java index 9a9b6f6..2746807 100755 --- a/src/MCview/PDBfile.java +++ b/src/MCview/PDBfile.java @@ -46,10 +46,10 @@ public class PDBfile extends StructureFile } public PDBfile(boolean addAlignmentAnnotations, boolean predictSecStr, - boolean externalSecStr, String file, String protocol) + boolean externalSecStr, String dataObject, String protocol) throws IOException { - super(false, file, protocol); + super(false, dataObject, protocol); addSettings(addAlignmentAnnotations, predictSecStr, externalSecStr); doParse(); } diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java index 4201f43..c0c7c46 100644 --- a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java +++ b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java @@ -199,8 +199,9 @@ public class ChimeraManager return null; } - List 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 sendChimeraCommand(String command, boolean reply) { - // System.out.println("chimeradebug>> " + command); + // System.out.println("chimeradebug>> " + command); if (!isChimeraLaunched() || command == null || "".equals(command.trim())) { diff --git a/src/jalview/analysis/AAFrequency.java b/src/jalview/analysis/AAFrequency.java index 3d61b11..fb49541 100755 --- a/src/jalview/analysis/AAFrequency.java +++ b/src/jalview/analysis/AAFrequency.java @@ -624,8 +624,11 @@ public class AAFrequency String modalCodon = String.valueOf(CodingUtils .decodeCodon(modalCodonEncoded)); if (sortedCodonCounts.length > 1 - && sortedCodonCounts[codons.length - 2] == modalCodonEncoded) + && sortedCodonCounts[codons.length - 2] == sortedCodonCounts[codons.length - 1]) { + /* + * two or more codons share the modal count + */ modalCodon = "+"; } float pid = sortedCodonCounts[sortedCodonCounts.length - 1] * 100 diff --git a/src/jalview/analysis/AlignmentUtils.java b/src/jalview/analysis/AlignmentUtils.java index 42a1201..ea330d8 100644 --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@ -24,6 +24,7 @@ import static jalview.io.gff.GffConstants.CLINICAL_SIGNIFICANCE; import jalview.datamodel.AlignedCodon; import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; @@ -38,8 +39,10 @@ import jalview.io.gff.SequenceOntologyFactory; import jalview.io.gff.SequenceOntologyI; import jalview.schemes.ResidueProperties; import jalview.util.Comparison; +import jalview.util.DBRefUtils; import jalview.util.MapList; import jalview.util.MappingUtils; +import jalview.util.StringUtils; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; @@ -69,6 +72,7 @@ import java.util.TreeMap; public class AlignmentUtils { + private static final int CODON_LENGTH = 3; private static final String SEQUENCE_VARIANT = "sequence_variant:"; private static final String ID = "ID"; @@ -76,15 +80,16 @@ public class AlignmentUtils * A data model to hold the 'normal' base value at a position, and an optional * sequence variant feature */ - static class DnaVariant + static final class DnaVariant { - String base; + final String base; SequenceFeature variant; DnaVariant(String nuc) { base = nuc; + variant = null; } DnaVariant(String nuc, SequenceFeature var) @@ -92,6 +97,11 @@ public class AlignmentUtils base = nuc; variant = var; } + + public String getSource() + { + return variant == null ? null : variant.getFeatureGroup(); + } } /** @@ -424,7 +434,7 @@ public class AlignmentUtils /* * cdnaStart/End, proteinStartEnd are base 1 (for dataset sequence mapping) */ - final int mappedLength = 3 * aaSeqChars.length; + final int mappedLength = CODON_LENGTH * aaSeqChars.length; int cdnaLength = cdnaSeqChars.length; int cdnaStart = cdnaSeq.getStart(); int cdnaEnd = cdnaSeq.getEnd(); @@ -436,14 +446,14 @@ public class AlignmentUtils */ if (cdnaLength != mappedLength && cdnaLength > 2) { - String lastCodon = String.valueOf(cdnaSeqChars, cdnaLength - 3, 3) + String lastCodon = String.valueOf(cdnaSeqChars, cdnaLength - CODON_LENGTH, CODON_LENGTH) .toUpperCase(); for (String stop : ResidueProperties.STOP) { if (lastCodon.equals(stop)) { - cdnaEnd -= 3; - cdnaLength -= 3; + cdnaEnd -= CODON_LENGTH; + cdnaLength -= CODON_LENGTH; break; } } @@ -455,12 +465,12 @@ public class AlignmentUtils int startOffset = 0; if (cdnaLength != mappedLength && cdnaLength > 2 - && String.valueOf(cdnaSeqChars, 0, 3).toUpperCase() + && String.valueOf(cdnaSeqChars, 0, CODON_LENGTH).toUpperCase() .equals(ResidueProperties.START)) { - startOffset += 3; - cdnaStart += 3; - cdnaLength -= 3; + startOffset += CODON_LENGTH; + cdnaStart += CODON_LENGTH; + cdnaLength -= CODON_LENGTH; } if (translatesAs(cdnaSeqChars, startOffset, aaSeqChars)) @@ -469,7 +479,7 @@ public class AlignmentUtils * protein is translation of dna (+/- start/stop codons) */ MapList map = new MapList(new int[] { cdnaStart, cdnaEnd }, new int[] - { proteinStart, proteinEnd }, 3, 1); + { proteinStart, proteinEnd }, CODON_LENGTH, 1); return map; } @@ -500,9 +510,9 @@ public class AlignmentUtils int aaPos = 0; int dnaPos = cdnaStart; for (; dnaPos < cdnaSeqChars.length - 2 - && aaPos < aaSeqChars.length; dnaPos += 3, aaPos++) + && aaPos < aaSeqChars.length; dnaPos += CODON_LENGTH, aaPos++) { - String codon = String.valueOf(cdnaSeqChars, dnaPos, 3); + String codon = String.valueOf(cdnaSeqChars, dnaPos, CODON_LENGTH); final String translated = ResidueProperties.codonTranslate(codon); /* @@ -538,9 +548,9 @@ public class AlignmentUtils { return true; } - if (dnaPos == cdnaSeqChars.length - 3) + if (dnaPos == cdnaSeqChars.length - CODON_LENGTH) { - String codon = String.valueOf(cdnaSeqChars, dnaPos, 3); + String codon = String.valueOf(cdnaSeqChars, dnaPos, CODON_LENGTH); if ("STOP".equals(ResidueProperties.codonTranslate(codon))) { return true; @@ -848,6 +858,11 @@ public class AlignmentUtils */ public static int alignProteinAsDna(AlignmentI protein, AlignmentI dna) { + if (protein.isNucleotide() || !dna.isNucleotide()) + { + System.err.println("Wrong alignment type in alignProteinAsDna"); + return 0; + } List unmappedProtein = new ArrayList(); Map> alignedCodons = buildCodonColumnsMap( protein, dna, unmappedProtein); @@ -855,6 +870,177 @@ public class AlignmentUtils } /** + * Realigns the given dna to match the alignment of the protein, using codon + * mappings to translate aligned peptide positions to codons. + * + * Always produces a padded CDS alignment. + * + * @param dna + * the alignment whose sequences are realigned by this method + * @param protein + * the protein alignment whose alignment we are 'copying' + * @return the number of sequences that were realigned + */ + public static int alignCdsAsProtein(AlignmentI dna, AlignmentI protein) + { + if (protein.isNucleotide() || !dna.isNucleotide()) + { + System.err.println("Wrong alignment type in alignProteinAsDna"); + return 0; + } + // todo: implement this + List mappings = protein.getCodonFrames(); + int alignedCount = 0; + int width = 0; // alignment width for padding CDS + for (SequenceI dnaSeq : dna.getSequences()) + { + if (alignCdsSequenceAsProtein(dnaSeq, protein, mappings, + dna.getGapCharacter())) + { + alignedCount++; + } + width = Math.max(dnaSeq.getLength(), width); + } + int oldwidth; + int diff; + for (SequenceI dnaSeq : dna.getSequences()) + { + oldwidth = dnaSeq.getLength(); + diff = width - oldwidth; + if (diff > 0) + { + dnaSeq.insertCharAt(oldwidth, diff, dna.getGapCharacter()); + } + } + return alignedCount; + } + + /** + * Helper method to align (if possible) the dna sequence to match the + * alignment of a mapped protein sequence. This is currently limited to + * handling coding sequence only. + * + * @param cdsSeq + * @param protein + * @param mappings + * @param gapChar + * @return + */ + static boolean alignCdsSequenceAsProtein(SequenceI cdsSeq, + AlignmentI protein, List mappings, char gapChar) + { + SequenceI cdsDss = cdsSeq.getDatasetSequence(); + if (cdsDss == null) + { + System.err + .println("alignCdsSequenceAsProtein needs aligned sequence!"); + return false; + } + + List dnaMappings = MappingUtils + .findMappingsForSequence(cdsSeq, mappings); + for (AlignedCodonFrame mapping : dnaMappings) + { + SequenceI peptide = mapping.findAlignedSequence(cdsSeq, protein); + if (peptide != null) + { + int peptideLength = peptide.getLength(); + Mapping map = mapping.getMappingBetween(cdsSeq, peptide); + if (map != null) + { + MapList mapList = map.getMap(); + if (map.getTo() == peptide.getDatasetSequence()) + { + mapList = mapList.getInverse(); + } + int cdsLength = cdsDss.getLength(); + int mappedFromLength = MappingUtils.getLength(mapList + .getFromRanges()); + int mappedToLength = MappingUtils + .getLength(mapList.getToRanges()); + boolean addStopCodon = (cdsLength == mappedFromLength * CODON_LENGTH + CODON_LENGTH) + || (peptide.getDatasetSequence().getLength() == mappedFromLength - 1); + if (cdsLength != mappedToLength && !addStopCodon) + { + System.err + .println(String + .format("Can't align cds as protein (length mismatch %d/%d): %s", + cdsLength, mappedToLength, + cdsSeq.getName())); + } + + /* + * pre-fill the aligned cds sequence with gaps + */ + char[] alignedCds = new char[peptideLength * CODON_LENGTH + + (addStopCodon ? CODON_LENGTH : 0)]; + Arrays.fill(alignedCds, gapChar); + + /* + * walk over the aligned peptide sequence and insert mapped + * codons for residues in the aligned cds sequence + */ + char[] alignedPeptide = peptide.getSequence(); + char[] nucleotides = cdsDss.getSequence(); + int copiedBases = 0; + int cdsStart = cdsDss.getStart(); + int proteinPos = peptide.getStart() - 1; + int cdsCol = 0; + for (char residue : alignedPeptide) + { + if (Comparison.isGap(residue)) + { + cdsCol += CODON_LENGTH; + } + else + { + proteinPos++; + int[] codon = mapList.locateInTo(proteinPos, proteinPos); + if (codon == null) + { + // e.g. incomplete start codon, X in peptide + cdsCol += CODON_LENGTH; + } + else + { + for (int j = codon[0]; j <= codon[1]; j++) + { + char mappedBase = nucleotides[j - cdsStart]; + alignedCds[cdsCol++] = mappedBase; + copiedBases++; + } + } + } + } + + /* + * append stop codon if not mapped from protein, + * closing it up to the end of the mapped sequence + */ + if (copiedBases == nucleotides.length - CODON_LENGTH) + { + for (int i = alignedCds.length - 1; i >= 0; i--) + { + if (!Comparison.isGap(alignedCds[i])) + { + cdsCol = i + 1; // gap just after end of sequence + break; + } + } + for (int i = nucleotides.length - CODON_LENGTH; i < nucleotides.length; i++) + { + alignedCds[cdsCol++] = nucleotides[i]; + } + } + cdsSeq.setSequence(new String(alignedCds)); + return true; + } + } + } + return false; + } + + /** * Builds a map whose key is an aligned codon position (3 alignment column * numbers base 0), and whose value is a map from protein sequence to each * protein's peptide residue for that codon. The map generates an ordering of @@ -1331,15 +1517,19 @@ public class AlignmentUtils Collection types, List forSequences, boolean anyType, boolean doShow) { - for (AlignmentAnnotation aa : al.getAlignmentAnnotation()) + AlignmentAnnotation[] anns = al.getAlignmentAnnotation(); + if (anns != null) { - if (anyType || types.contains(aa.label)) + for (AlignmentAnnotation aa : anns) { - if ((aa.sequenceRef != null) - && (forSequences == null || forSequences - .contains(aa.sequenceRef))) + if (anyType || types.contains(aa.label)) { - aa.visible = doShow; + if ((aa.sequenceRef != null) + && (forSequences == null || forSequences + .contains(aa.sequenceRef))) + { + aa.visible = doShow; + } } } } @@ -1398,75 +1588,292 @@ public class AlignmentUtils * added to the alignment dataset. * * @param dna - * aligned dna sequences - * @param mappings - * from dna to protein - * @param al + * aligned nucleotide (dna or cds) sequences + * @param dataset + * the alignment dataset the sequences belong to + * @param products + * (optional) to restrict results to CDS that map to specified + * protein products * @return an alignment whose sequences are the cds-only parts of the dna * sequences (or null if no mappings are found) */ public static AlignmentI makeCdsAlignment(SequenceI[] dna, - List mappings, AlignmentI al) + AlignmentI dataset, SequenceI[] products) { + if (dataset == null || dataset.getDataset() != null) + { + throw new IllegalArgumentException( + "IMPLEMENTATION ERROR: dataset.getDataset() must be null!"); + } + List foundSeqs = new ArrayList(); List cdsSeqs = new ArrayList(); - - for (SequenceI seq : dna) + List mappings = dataset.getCodonFrames(); + HashSet productSeqs = null; + if (products != null) { - AlignedCodonFrame cdsMappings = new AlignedCodonFrame(); + productSeqs = new HashSet(); + for (SequenceI seq : products) + { + productSeqs.add(seq.getDatasetSequence() == null ? seq : seq + .getDatasetSequence()); + } + } + + /* + * Construct CDS sequences from mappings on the alignment dataset. + * The logic is: + * - find the protein product(s) mapped to from each dna sequence + * - if the mapping covers the whole dna sequence (give or take start/stop + * codon), take the dna as the CDS sequence + * - else search dataset mappings for a suitable dna sequence, i.e. one + * whose whole sequence is mapped to the protein + * - if no sequence found, construct one from the dna sequence and mapping + * (and add it to dataset so it is found if this is repeated) + */ + for (SequenceI dnaSeq : dna) + { + SequenceI dnaDss = dnaSeq.getDatasetSequence() == null ? dnaSeq + : dnaSeq.getDatasetSequence(); + List seqMappings = MappingUtils - .findMappingsForSequence(seq, mappings); - List alignmentMappings = al.getCodonFrames(); + .findMappingsForSequence(dnaSeq, mappings); for (AlignedCodonFrame mapping : seqMappings) { - for (Mapping aMapping : mapping.getMappingsFromSequence(seq)) + List mappingsFromSequence = mapping + .getMappingsFromSequence(dnaSeq); + + for (Mapping aMapping : mappingsFromSequence) { - SequenceI cdsSeq = makeCdsSequence(seq.getDatasetSequence(), - aMapping); + MapList mapList = aMapping.getMap(); + if (mapList.getFromRatio() == 1) + { + /* + * not a dna-to-protein mapping (likely dna-to-cds) + */ + continue; + } + + /* + * skip if mapping is not to one of the target set of proteins + */ + SequenceI proteinProduct = aMapping.getTo(); + if (productSeqs != null && !productSeqs.contains(proteinProduct)) + { + continue; + } + + /* + * try to locate the CDS from the dataset mappings; + * guard against duplicate results (for the case that protein has + * dbrefs to both dna and cds sequences) + */ + SequenceI cdsSeq = findCdsForProtein(mappings, dnaSeq, + seqMappings, aMapping); + if (cdsSeq != null) + { + if (!foundSeqs.contains(cdsSeq)) + { + foundSeqs.add(cdsSeq); + SequenceI derivedSequence = cdsSeq.deriveSequence(); + cdsSeqs.add(derivedSequence); + if (!dataset.getSequences().contains(cdsSeq)) + { + dataset.addSequence(cdsSeq); + } + } + continue; + } + + /* + * didn't find mapped CDS sequence - construct it and add + * its dataset sequence to the dataset + */ + cdsSeq = makeCdsSequence(dnaSeq.getDatasetSequence(), aMapping, + dataset).deriveSequence(); + // cdsSeq has a name constructed as CDS| + // will be either the accession for the coding sequence, + // marked in the /via/ dbref to the protein product accession + // or it will be the original nucleotide accession. + SequenceI cdsSeqDss = cdsSeq.getDatasetSequence(); + cdsSeqs.add(cdsSeq); - + + if (!dataset.getSequences().contains(cdsSeqDss)) + { + // check if this sequence is a newly created one + // so needs adding to the dataset + dataset.addSequence(cdsSeqDss); + } + /* * add a mapping from CDS to the (unchanged) mapped to range */ List cdsRange = Collections.singletonList(new int[] { 1, cdsSeq.getLength() }); - MapList map = new MapList(cdsRange, aMapping.getMap() - .getToRanges(), aMapping.getMap().getFromRatio(), - aMapping.getMap().getToRatio()); - cdsMappings.addMap(cdsSeq, aMapping.getTo(), map); + MapList cdsToProteinMap = new MapList(cdsRange, mapList.getToRanges(), + mapList.getFromRatio(), mapList.getToRatio()); + AlignedCodonFrame cdsToProteinMapping = new AlignedCodonFrame(); + cdsToProteinMapping.addMap(cdsSeqDss, proteinProduct, + cdsToProteinMap); /* + * guard against duplicating the mapping if repeating this action + */ + if (!mappings.contains(cdsToProteinMapping)) + { + mappings.add(cdsToProteinMapping); + } + + propagateDBRefsToCDS(cdsSeqDss, dnaSeq.getDatasetSequence(), + proteinProduct, aMapping); + /* * add another mapping from original 'from' range to CDS */ - map = new MapList(aMapping.getMap().getFromRanges(), cdsRange, 1, + AlignedCodonFrame dnaToCdsMapping = new AlignedCodonFrame(); + MapList dnaToCdsMap = new MapList(mapList.getFromRanges(), + cdsRange, 1, 1); - cdsMappings.addMap(seq.getDatasetSequence(), cdsSeq, map); + dnaToCdsMapping.addMap(dnaSeq.getDatasetSequence(), cdsSeqDss, + dnaToCdsMap); + if (!mappings.contains(dnaToCdsMapping)) + { + mappings.add(dnaToCdsMapping); + } - alignmentMappings.add(cdsMappings); + /* + * add DBRef with mapping from protein to CDS + * (this enables Get Cross-References from protein alignment) + * This is tricky because we can't have two DBRefs with the + * same source and accession, so need a different accession for + * the CDS from the dna sequence + */ + + // specific use case: + // Genomic contig ENSCHR:1, contains coding regions for ENSG01, + // ENSG02, ENSG03, with transcripts and products similarly named. + // cannot add distinct dbrefs mapping location on ENSCHR:1 to ENSG01 + + // JBPNote: ?? can't actually create an example that demonstrates we + // need to + // synthesize an xref. + + for (DBRefEntry primRef : dnaDss.getPrimaryDBRefs()) + { + // creates a complementary cross-reference to the source sequence's + // primary reference. + + DBRefEntry cdsCrossRef = new DBRefEntry(primRef.getSource(), + primRef.getSource() + ":" + primRef.getVersion(), + primRef.getAccessionId()); + cdsCrossRef + .setMap(new Mapping(dnaDss, new MapList(dnaToCdsMap))); + cdsSeqDss.addDBRef(cdsCrossRef); + + // problem here is that the cross-reference is synthesized - + // cdsSeq.getName() may be like 'CDS|dnaaccession' or + // 'CDS|emblcdsacc' + // assuming cds version same as dna ?!? + + DBRefEntry proteinToCdsRef = new DBRefEntry( + primRef.getSource(), primRef.getVersion(), + cdsSeq.getName()); + // + proteinToCdsRef.setMap(new Mapping(cdsSeqDss, cdsToProteinMap + .getInverse())); + proteinProduct.addDBRef(proteinToCdsRef); + } /* * transfer any features on dna that overlap the CDS */ - transferFeatures(seq, cdsSeq, map, null, SequenceOntologyI.CDS); + transferFeatures(dnaSeq, cdsSeq, cdsToProteinMap, null, + SequenceOntologyI.CDS); } } } + AlignmentI cds = new Alignment(cdsSeqs.toArray(new SequenceI[cdsSeqs + .size()])); + cds.setDataset(dataset); + + return cds; + } + + /** + * A helper method that finds a CDS sequence in the alignment dataset that is + * mapped to the given protein sequence, and either is, or has a mapping from, + * the given dna sequence. + * + * @param mappings + * set of all mappings on the dataset + * @param dnaSeq + * a dna (or cds) sequence we are searching from + * @param seqMappings + * the set of mappings involving dnaSeq + * @param aMapping + * an initial candidate from seqMappings + * @return + */ + static SequenceI findCdsForProtein(List mappings, + SequenceI dnaSeq, List seqMappings, + Mapping aMapping) + { + /* + * TODO a better dna-cds-protein mapping data representation to allow easy + * navigation; until then this clunky looping around lists of mappings + */ + SequenceI seqDss = dnaSeq.getDatasetSequence() == null ? dnaSeq + : dnaSeq.getDatasetSequence(); + SequenceI proteinProduct = aMapping.getTo(); + + /* + * is this mapping from the whole dna sequence (i.e. CDS)? + * allowing for possible stop codon on dna but not peptide + */ + int mappedFromLength = MappingUtils.getLength(aMapping.getMap() + .getFromRanges()); + int dnaLength = seqDss.getLength(); + if (mappedFromLength == dnaLength || mappedFromLength == dnaLength - CODON_LENGTH) + { + return seqDss; + } + /* - * add CDS seqs to shared dataset + * looks like we found the dna-to-protein mapping; search for the + * corresponding cds-to-protein mapping */ - Alignment dataset = al.getDataset(); - for (SequenceI seq : cdsSeqs) + List mappingsToPeptide = MappingUtils + .findMappingsForSequence(proteinProduct, mappings); + for (AlignedCodonFrame acf : mappingsToPeptide) { - if (!dataset.getSequences().contains(seq.getDatasetSequence())) + for (SequenceToSequenceMapping map : acf.getMappings()) { - dataset.addSequence(seq.getDatasetSequence()); + Mapping mapping = map.getMapping(); + if (mapping != aMapping && mapping.getMap().getFromRatio() == CODON_LENGTH + && proteinProduct == mapping.getTo() + && seqDss != map.getFromSeq()) + { + mappedFromLength = MappingUtils.getLength(mapping.getMap() + .getFromRanges()); + if (mappedFromLength == map.getFromSeq().getLength()) + { + /* + * found a 3:1 mapping to the protein product which covers + * the whole dna sequence i.e. is from CDS; finally check it + * is from the dna start sequence + */ + SequenceI cdsSeq = map.getFromSeq(); + List dnaToCdsMaps = MappingUtils + .findMappingsForSequence(cdsSeq, seqMappings); + if (!dnaToCdsMaps.isEmpty()) + { + return cdsSeq; + } + } + } } } - AlignmentI cds = new Alignment(cdsSeqs.toArray(new SequenceI[cdsSeqs - .size()])); - cds.setDataset(dataset); - - return cds; + return null; } /** @@ -1476,9 +1883,14 @@ public class AlignmentUtils * * @param seq * @param mapping - * @return + * @param dataset + * - existing dataset. We check for sequences that look like the CDS + * we are about to construct, if one exists already, then we will + * just return that one. + * @return CDS sequence (as a dataset sequence) */ - static SequenceI makeCdsSequence(SequenceI seq, Mapping mapping) + static SequenceI makeCdsSequence(SequenceI seq, Mapping mapping, + AlignmentI dataset) { char[] seqChars = seq.getSequence(); List fromRanges = mapping.getMap().getFromRanges(); @@ -1505,14 +1917,132 @@ public class AlignmentUtils } } } + + /* + * assign 'from id' held in the mapping if set (e.g. EMBL protein_id), + * else generate a sequence name + */ + String mapFromId = mapping.getMappedFromId(); + String seqId = "CDS|" + (mapFromId != null ? mapFromId : seq.getName()); + SequenceI newSeq = new Sequence(seqId, newSeqChars, 1, newPos); + if (dataset != null) + { + SequenceI[] matches = dataset.findSequenceMatch(newSeq.getName()); + if (matches != null) + { + boolean matched = false; + for (SequenceI mtch : matches) + { + if (mtch.getStart() != newSeq.getStart()) + { + continue; + } + if (mtch.getEnd() != newSeq.getEnd()) + { + continue; + } + if (!Arrays.equals(mtch.getSequence(), newSeq.getSequence())) + { + continue; + } + if (!matched) + { + matched = true; + newSeq = mtch; + } + else + { + System.err + .println("JAL-2154 regression: warning - found (and ignnored a duplicate CDS sequence):" + + mtch.toString()); + } + } + } + } + // newSeq.setDescription(mapFromId); - SequenceI newSeq = new Sequence(seq.getName() + "|" - + mapping.getTo().getName(), newSeqChars, 1, newPos); - newSeq.createDatasetSequence(); return newSeq; } /** + * add any DBRefEntrys to cdsSeq from contig that have a Mapping congruent to + * the given mapping. + * + * @param cdsSeq + * @param contig + * @param mapping + * @return list of DBRefEntrys added. + */ + public static List propagateDBRefsToCDS(SequenceI cdsSeq, + SequenceI contig, SequenceI proteinProduct, Mapping mapping) + { + + // gather direct refs from contig congrent with mapping + List direct = new ArrayList(); + HashSet directSources = new HashSet(); + if (contig.getDBRefs() != null) + { + for (DBRefEntry dbr : contig.getDBRefs()) + { + if (dbr.hasMap() && dbr.getMap().getMap().isTripletMap()) + { + MapList map = dbr.getMap().getMap(); + // check if map is the CDS mapping + if (mapping.getMap().equals(map)) + { + direct.add(dbr); + directSources.add(dbr.getSource()); + } + } + } + } + DBRefEntry[] onSource = DBRefUtils.selectRefs( + proteinProduct.getDBRefs(), + directSources.toArray(new String[0])); + List propagated = new ArrayList(); + + // and generate appropriate mappings + for (DBRefEntry cdsref : direct) + { + // clone maplist and mapping + MapList cdsposmap = new MapList(Arrays.asList(new int[][] { new int[] + { cdsSeq.getStart(), cdsSeq.getEnd() } }), cdsref.getMap().getMap() + .getToRanges(), 3, 1); + Mapping cdsmap = new Mapping(cdsref.getMap().getTo(), cdsref.getMap() + .getMap()); + + // create dbref + DBRefEntry newref = new DBRefEntry(cdsref.getSource(), + cdsref.getVersion(), cdsref.getAccessionId(), new Mapping( + cdsmap.getTo(), cdsposmap)); + + // and see if we can map to the protein product for this mapping. + // onSource is the filtered set of accessions on protein that we are + // tranferring, so we assume accession is the same. + if (cdsmap.getTo() == null && onSource != null) + { + List sourceRefs = DBRefUtils.searchRefs(onSource, + cdsref.getAccessionId()); + if (sourceRefs != null) + { + for (DBRefEntry srcref : sourceRefs) + { + if (srcref.getSource().equalsIgnoreCase(cdsref.getSource())) + { + // we have found a complementary dbref on the protein product, so + // update mapping's getTo + newref.getMap().setTo(proteinProduct); + } + } + } + } + cdsSeq.addDBRef(newref); + propagated.add(newref); + } + return propagated; + } + + /** * Transfers co-located features on 'fromSeq' to 'toSeq', adjusting the * feature start/end ranges, optionally omitting specified feature types. * Returns the number of features copied. @@ -1641,7 +2171,7 @@ public class AlignmentUtils /* * dna length should map to protein (or protein plus stop codon) */ - int codesForResidues = mappedDnaLength / 3; + int codesForResidues = mappedDnaLength / CODON_LENGTH; if (codesForResidues == (proteinLength + 1)) { // assuming extra codon is for STOP and not in peptide @@ -1650,7 +2180,7 @@ public class AlignmentUtils if (codesForResidues == proteinLength) { proteinRange.add(new int[] { proteinStart, proteinEnd }); - return new MapList(ranges, proteinRange, 3, 1); + return new MapList(ranges, proteinRange, CODON_LENGTH, 1); } return null; } @@ -1794,17 +2324,20 @@ public class AlignmentUtils * 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() - { - @Override - public int compare(SequenceFeature o1, SequenceFeature o2) + if (peptide.getSequenceFeatures() != null) + { + Arrays.sort(peptide.getSequenceFeatures(), + new Comparator() { - int c = Integer.compare(o1.getBegin(), o2.getBegin()); - return c == 0 ? Integer.compare(o1.getEnd(), o2.getEnd()) - : c; - } - }); + @Override + public int compare(SequenceFeature o1, SequenceFeature o2) + { + int c = Integer.compare(o1.getBegin(), o2.getBegin()); + return c == 0 ? Integer.compare(o1.getEnd(), o2.getEnd()) + : c; + } + }); + } return count; } @@ -1923,15 +2456,19 @@ public class AlignmentUtils * are currently ignored here */ String trans = codon.contains("-") ? "-" - : (codon.length() > 3 ? null : ResidueProperties + : (codon.length() > CODON_LENGTH ? null : ResidueProperties .codonTranslate(codon)); if (trans != null && !trans.equals(residue)) { - String desc = residue + "->" + trans; + 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, null); + peptidePos, 0f, var.getSource()); StringBuilder attributes = new StringBuilder(32); String id = (String) var.variant.getValue(ID); if (id != null) @@ -1942,7 +2479,7 @@ public class AlignmentUtils } sf.setValue(ID, id); attributes.append(ID).append("=").append(id); - // TODO handle other species variants + // TODO handle other species variants JAL-2064 StringBuilder link = new StringBuilder(32); try { @@ -1981,6 +2518,7 @@ public class AlignmentUtils * @param dnaToProtein * @return */ + @SuppressWarnings("unchecked") static LinkedHashMap[]> buildDnaVariantsMap( SequenceI dnaSeq, MapList dnaToProtein) { @@ -2024,7 +2562,7 @@ public class AlignmentUtils List[] codonVariants = variants.get(peptidePosition); if (codonVariants == null) { - codonVariants = new ArrayList[3]; + codonVariants = new ArrayList[CODON_LENGTH]; codonVariants[0] = new ArrayList(); codonVariants[1] = new ArrayList(); codonVariants[2] = new ArrayList(); @@ -2058,7 +2596,7 @@ public class AlignmentUtils /* * save nucleotide (and any variant) for each codon position */ - for (int codonPos = 0; codonPos < 3; codonPos++) + for (int codonPos = 0; codonPos < CODON_LENGTH; codonPos++) { String nucleotide = String.valueOf( dnaSeq.getCharAt(codon[codonPos] - dnaStart)) @@ -2102,13 +2640,16 @@ public class AlignmentUtils * * @param seqs * @param xrefs + * @param dataset + * the alignment dataset shared by the new copy * @return */ public static AlignmentI makeCopyAlignment(SequenceI[] seqs, - SequenceI[] xrefs) + SequenceI[] xrefs, AlignmentI dataset) { AlignmentI copy = new Alignment(new Alignment(seqs)); - + copy.setDataset(dataset); + boolean isProtein = !copy.isNucleotide(); SequenceIdMatcher matcher = new SequenceIdMatcher(seqs); if (xrefs != null) { @@ -2119,7 +2660,8 @@ public class AlignmentUtils { for (DBRefEntry dbref : dbrefs) { - if (dbref.getMap() == null || dbref.getMap().getTo() == null) + if (dbref.getMap() == null || dbref.getMap().getTo() == null + || dbref.getMap().getTo().isProtein() != isProtein) { continue; } @@ -2153,19 +2695,32 @@ public class AlignmentUtils */ public static int alignAs(AlignmentI unaligned, AlignmentI aligned) { + /* + * easy case - aligning a copy of aligned sequences + */ + if (alignAsSameSequences(unaligned, aligned)) + { + return unaligned.getHeight(); + } + + /* + * fancy case - aligning via mappings between sequences + */ List unmapped = new ArrayList(); Map> columnMap = buildMappedColumnsMap( unaligned, aligned, unmapped); int width = columnMap.size(); char gap = unaligned.getGapCharacter(); int realignedCount = 0; + // TODO: verify this loop scales sensibly for very wide/high alignments for (SequenceI seq : unaligned.getSequences()) { if (!unmapped.contains(seq)) { char[] newSeq = new char[width]; - Arrays.fill(newSeq, gap); + Arrays.fill(newSeq, gap); // JBPComment - doubt this is faster than the + // Integer iteration below int newCol = 0; int lastCol = 0; @@ -2197,6 +2752,7 @@ public class AlignmentUtils System.arraycopy(newSeq, 0, tmp, 0, lastCol + 1); newSeq = tmp; } + // TODO: optimise SequenceI to avoid char[]->String->char[] seq.setSequence(String.valueOf(newSeq)); realignedCount++; } @@ -2205,6 +2761,72 @@ public class AlignmentUtils } /** + * If unaligned and aligned sequences share the same dataset sequences, then + * simply copies the aligned sequences to the unaligned sequences and returns + * true; else returns false + * + * @param unaligned + * - sequences to be aligned based on aligned + * @param aligned + * - 'guide' alignment containing sequences derived from same dataset + * as unaligned + * @return + */ + static boolean alignAsSameSequences(AlignmentI unaligned, + AlignmentI aligned) + { + if (aligned.getDataset() == null || unaligned.getDataset() == null) + { + return false; // should only pass alignments with datasets here + } + + // map from dataset sequence to alignment sequence(s) + Map> alignedDatasets = new HashMap>(); + for (SequenceI seq : aligned.getSequences()) + { + SequenceI ds = seq.getDatasetSequence(); + if (alignedDatasets.get(ds) == null) + { + alignedDatasets.put(ds, new ArrayList()); + } + alignedDatasets.get(ds).add(seq); + } + + /* + * first pass - check whether all sequences to be aligned share a dataset + * sequence with an aligned sequence + */ + for (SequenceI seq : unaligned.getSequences()) + { + if (!alignedDatasets.containsKey(seq.getDatasetSequence())) + { + return false; + } + } + + /* + * second pass - copy aligned sequences; + * heuristic rule: pair off sequences in order for the case where + * more than one shares the same dataset sequence + */ + for (SequenceI seq : unaligned.getSequences()) + { + List alignedSequences = alignedDatasets.get(seq + .getDatasetSequence()); + // TODO: getSequenceAsString() will be deprecated in the future + // TODO: need to leave to SequenceI implementor to update gaps + seq.setSequence(alignedSequences.get(0).getSequenceAsString()); + if (alignedSequences.size() > 0) + { + // pop off aligned sequences (except the last one) + alignedSequences.remove(0); + } + } + + return true; + } + + /** * Returns a map whose key is alignment column number (base 1), and whose * values are a map of sequence characters in that column. * @@ -2218,13 +2840,13 @@ public class AlignmentUtils { /* * Map will hold, for each aligned column position, a map of - * {unalignedSequence, sequenceCharacter} at that position. + * {unalignedSequence, characterPerSequence} at that position. * TreeMap keeps the entries in ascending column order. */ Map> map = new TreeMap>(); /* - * r any sequences that have no mapping so can't be realigned + * record any sequences that have no mapping so can't be realigned */ unmapped.addAll(unaligned.getSequences()); @@ -2273,6 +2895,15 @@ public class AlignmentUtils return false; } + /* + * invert mapping if it is from unaligned to aligned sequence + */ + if (seqMap.getTo() == fromSeq.getDatasetSequence()) + { + seqMap = new Mapping(seq.getDatasetSequence(), seqMap.getMap() + .getInverse()); + } + char[] fromChars = fromSeq.getSequence(); int toStart = seq.getStart(); char[] toChars = seq.getSequence(); @@ -2306,7 +2937,8 @@ public class AlignmentUtils * of the next character of the mapped-to sequence; stop when all * the characters of the range have been counted */ - while (mappedCharPos <= range[1]) + while (mappedCharPos <= range[1] && fromCol <= fromChars.length + && fromCol >= 0) { if (!Comparison.isGap(fromChars[fromCol - 1])) { diff --git a/src/jalview/analysis/Conservation.java b/src/jalview/analysis/Conservation.java index d4ae57d..21d990c 100755 --- a/src/jalview/analysis/Conservation.java +++ b/src/jalview/analysis/Conservation.java @@ -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 @@ -679,7 +693,7 @@ public class Conservation qmax = qualityRange[1].floatValue(); } - for (int i = 0; i < alWidth; i++) + for (int i = istart; i < alWidth; i++) { float value = 0; diff --git a/src/jalview/analysis/CrossRef.java b/src/jalview/analysis/CrossRef.java index 7e77fc1..1295b46 100644 --- a/src/jalview/analysis/CrossRef.java +++ b/src/jalview/analysis/CrossRef.java @@ -24,23 +24,21 @@ import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; -import jalview.datamodel.DBRefSource; import jalview.datamodel.Mapping; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.util.DBRefUtils; import jalview.util.MapList; -import jalview.ws.SequenceFetcher; +import jalview.ws.SequenceFetcherFactory; import jalview.ws.seqfetcher.ASequenceFetcher; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; -import java.util.Vector; /** - * Functions for cross-referencing sequence databases. user must first specify - * if cross-referencing from protein or dna (set dna==true) + * Functions for cross-referencing sequence databases. * * @author JimP * @@ -48,195 +46,173 @@ import java.util.Vector; public class CrossRef { /* - * A sub-class that ignores Parent attribute when comparing sequence - * features. This avoids 'duplicate' CDS features that only - * differ in their parent Transcript ids. + * the dataset of the alignment for which we are searching for + * cross-references; in some cases we may resolve xrefs by + * searching in the dataset */ - class MySequenceFeature extends SequenceFeature - { - private SequenceFeature feat; + private AlignmentI dataset; - MySequenceFeature(SequenceFeature sf) - { - this.feat = sf; - } + /* + * the sequences for which we are seeking cross-references + */ + private SequenceI[] fromSeqs; - @Override - public boolean equals(Object o) - { - return feat.equals(o, true); - } - } + /** + * matcher built from dataset + */ + SequenceIdMatcher matcher; /** - * Select just the DNA or protein references for a protein or dna sequence - * - * @param fromDna - * if true, select references from DNA (i.e. Protein databases), else - * DNA database references - * @param refs - * a set of references to select from - * @return + * sequences found by cross-ref searches to fromSeqs */ - public static DBRefEntry[] findXDbRefs(boolean fromDna, DBRefEntry[] refs) - { - return DBRefUtils.selectRefs(refs, fromDna ? DBRefSource.PROTEINDBS - : DBRefSource.DNACODINGDBS); - // could attempt to find other cross - // refs here - ie PDB xrefs - // (not dna, not protein seq) - } + List rseqs; /** - * @param dna - * true if seqs are DNA seqs + * Constructor + * * @param seqs - * @return a list of sequence database cross reference source types + * the sequences for which we are seeking cross-references + * @param ds + * the containing alignment dataset (may be searched to resolve + * cross-references) */ - public static String[] findSequenceXrefTypes(boolean dna, SequenceI[] seqs) + public CrossRef(SequenceI[] seqs, AlignmentI ds) { - return findSequenceXrefTypes(dna, seqs, null); + fromSeqs = seqs; + dataset = ds.getDataset() == null ? ds : ds.getDataset(); } /** - * Indirect references are references from other sequences from the dataset to - * any of the direct DBRefEntrys on the given sequences. + * Returns a list of distinct database sources for which sequences have either + *
          + *
        • a (dna-to-protein or protein-to-dna) cross-reference
        • + *
        • an indirect cross-reference - a (dna-to-protein or protein-to-dna) + * reference from another sequence in the dataset which has a cross-reference + * to a direct DBRefEntry on the given sequence
        • + *
        * * @param dna - * true if seqs are DNA seqs - * @param seqs - * @return a list of sequence database cross reference source types + * - when true, cross-references *from* dna returned. When false, + * cross-references *from* protein are returned + * @return */ - public static String[] findSequenceXrefTypes(boolean dna, - SequenceI[] seqs, AlignmentI dataset) + public List findXrefSourcesForSequences(boolean dna) { - String[] dbrefs = null; - List refs = new ArrayList(); - for (SequenceI seq : seqs) + List sources = new ArrayList(); + for (SequenceI seq : fromSeqs) { if (seq != null) { - SequenceI dss = seq; - while (dss.getDatasetSequence() != null) - { - dss = dss.getDatasetSequence(); - } - DBRefEntry[] rfs = findXDbRefs(dna, dss.getDBRefs()); - if (rfs != null) - { - for (DBRefEntry ref : rfs) - { - if (!refs.contains(ref.getSource())) - { - refs.add(ref.getSource()); - } - } - } - if (dataset != null) - { - // search for references to this sequence's direct references. - DBRefEntry[] lrfs = CrossRef.findXDbRefs(!dna, seq.getDBRefs()); - List rseqs = new ArrayList(); - CrossRef.searchDatasetXrefs(seq, !dna, lrfs, dataset, rseqs, - null); // don't need to specify codon frame for mapping here - for (SequenceI rs : rseqs) - { - DBRefEntry[] xrs = findXDbRefs(dna, rs.getDBRefs()); - if (xrs != null) - { - for (DBRefEntry ref : xrs) - { - if (!refs.contains(ref.getSource())) - { - refs.add(ref.getSource()); - } - } - } - // looks like copy and paste - change rfs to xrs? - // for (int r = 0; rfs != null && r < rfs.length; r++) - // { - // if (!refs.contains(rfs[r].getSource())) - // { - // refs.add(rfs[r].getSource()); - // } - // } - } - } + findXrefSourcesForSequence(seq, dna, sources); } } - if (refs.size() > 0) - { - dbrefs = new String[refs.size()]; - refs.toArray(dbrefs); - } - return dbrefs; + return sources; } - public static boolean hasCdnaMap(SequenceI[] seqs) + /** + * Returns a list of distinct database sources for which a sequence has either + *
          + *
        • a (dna-to-protein or protein-to-dna) cross-reference
        • + *
        • an indirect cross-reference - a (dna-to-protein or protein-to-dna) + * reference from another sequence in the dataset which has a cross-reference + * to a direct DBRefEntry on the given sequence
        • + *
        + * + * @param seq + * the sequence whose dbrefs we are searching against + * @param fromDna + * when true, context is DNA - so sources identifying protein + * products will be returned. + * @param sources + * a list of sources to add matches to + */ + void findXrefSourcesForSequence(SequenceI seq, boolean fromDna, + List sources) { - // TODO unused - remove? - String[] reftypes = findSequenceXrefTypes(false, seqs); - for (int s = 0; s < reftypes.length; s++) + /* + * first find seq's xrefs (dna-to-peptide or peptide-to-dna) + */ + DBRefEntry[] rfs = DBRefUtils.selectDbRefs(!fromDna, seq.getDBRefs()); + addXrefsToSources(rfs, sources); + if (dataset != null) { - if (reftypes.equals(DBRefSource.EMBLCDS)) + /* + * find sequence's direct (dna-to-dna, peptide-to-peptide) xrefs + */ + DBRefEntry[] lrfs = DBRefUtils.selectDbRefs(fromDna, seq.getDBRefs()); + List foundSeqs = new ArrayList(); + + /* + * find sequences in the alignment which xref one of these DBRefs + * i.e. is xref-ed to a common sequence identifier + */ + searchDatasetXrefs(fromDna, seq, lrfs, foundSeqs, null); + + /* + * add those sequences' (dna-to-peptide or peptide-to-dna) dbref sources + */ + for (SequenceI rs : foundSeqs) { - return true; - // no map + DBRefEntry[] xrs = DBRefUtils + .selectDbRefs(!fromDna, rs.getDBRefs()); + addXrefsToSources(xrs, sources); } } - return false; } - public static SequenceI[] getCdnaMap(SequenceI[] seqs) + /** + * Helper method that adds the source identifiers of some cross-references to + * a (non-redundant) list of database sources + * + * @param xrefs + * @param sources + */ + void addXrefsToSources(DBRefEntry[] xrefs, List sources) { - // TODO unused - remove? - Vector cseqs = new Vector(); - for (int s = 0; s < seqs.length; s++) + if (xrefs != null) { - DBRefEntry[] cdna = findXDbRefs(true, seqs[s].getDBRefs()); - for (int c = 0; c < cdna.length; c++) + for (DBRefEntry ref : xrefs) { - if (cdna[c].getSource().equals(DBRefSource.EMBLCDS)) + /* + * avoid duplication e.g. ENSEMBL and Ensembl + */ + String source = DBRefUtils.getCanonicalName(ref.getSource()); + if (!sources.contains(source)) { - System.err - .println("TODO: unimplemented sequence retrieval for coding region sequence."); - // TODO: retrieve CDS dataset sequences - // need global dataset sequence retriever/resolver to reuse refs - // and construct Mapping entry. - // insert gaps in CDS according to peptide gaps. - // add gapped sequence to cseqs + sources.add(source); } } } - if (cseqs.size() > 0) - { - SequenceI[] rsqs = new SequenceI[cseqs.size()]; - cseqs.copyInto(rsqs); - return rsqs; - } - return null; - } /** + * Attempts to find cross-references from the sequences provided in the + * constructor to the given source database. Cross-references may be found + *
          + *
        • in dbrefs on the sequence which hold a mapping to a sequence + *
            + *
          • provided with a fetched sequence (e.g. ENA translation), or
          • + *
          • populated previously after getting cross-references
          • + *
          + *
        • as other sequences in the alignment which share a dbref identifier with + * the sequence
        • + *
        • by fetching from the remote database
        • + *
        + * The cross-referenced sequences, and mappings to them, are added to the + * alignment dataset. * - * @param seqs - * sequences whose xrefs are being retrieved - * @param dna - * true if sequences are nucleotide * @param source - * @param al - * alignment to search for cross-referenced sequences (and possibly - * add to) - * @return products (as dataset sequences) + * @return cross-referenced sequences (as dataset sequences) */ - public static Alignment findXrefSequences(SequenceI[] seqs, - final boolean dna, final String source, AlignmentI al) + public Alignment findXrefSequences(String source, boolean fromDna) { - AlignmentI dataset = al.getDataset() == null ? al : al.getDataset(); - List rseqs = new ArrayList(); + + rseqs = new ArrayList(); AlignedCodonFrame cf = new AlignedCodonFrame(); - for (SequenceI seq : seqs) + matcher = new SequenceIdMatcher( + dataset.getSequences()); + + for (SequenceI seq : fromSeqs) { SequenceI dss = seq; while (dss.getDatasetSequence() != null) @@ -244,242 +220,461 @@ public class CrossRef dss = dss.getDatasetSequence(); } boolean found = false; - DBRefEntry[] xrfs = CrossRef.findXDbRefs(dna, dss.getDBRefs()); + DBRefEntry[] xrfs = DBRefUtils + .selectDbRefs(!fromDna, dss.getDBRefs()); + // ENST & ENSP comes in to both Protein and nucleotide, so we need to + // filter them + // out later. if ((xrfs == null || xrfs.length == 0) && dataset != null) { - System.out.println("Attempting to find ds Xrefs refs."); - // FIXME should be dss not seq here? - DBRefEntry[] lrfs = CrossRef.findXDbRefs(!dna, seq.getDBRefs()); - // less ambiguous would be a 'find primary dbRefEntry' method. - // filter for desired source xref here - found = CrossRef.searchDatasetXrefs(dss, !dna, lrfs, dataset, - rseqs, cf); + /* + * found no suitable dbrefs on sequence - look for sequences in the + * alignment which share a dbref with this one + */ + DBRefEntry[] lrfs = DBRefUtils.selectDbRefs(fromDna, + seq.getDBRefs()); + + /* + * find sequences (except this one!), of complementary type, + * which have a dbref to an accession id for this sequence, + * and add them to the results + */ + found = searchDatasetXrefs(fromDna, dss, lrfs, rseqs, cf); } - for (int r = 0; xrfs != null && r < xrfs.length; r++) + if (xrfs == null && !found) { - DBRefEntry xref = xrfs[r]; - if (source != null && !source.equals(xref.getSource())) - { - continue; - } - if (xref.hasMap()) + /* + * no dbref to source on this sequence or matched + * complementary sequence in the dataset + */ + continue; + } + List sourceRefs = DBRefUtils.searchRefsForSource(xrfs, + source); + Iterator refIterator = sourceRefs.iterator(); + // At this point, if we are retrieving Ensembl, we still don't filter out + // ENST when looking for protein crossrefs. + while (refIterator.hasNext()) + { + DBRefEntry xref = refIterator.next(); + found = false; + // we're only interested in coding cross-references, not + // locus->transcript + if (xref.hasMap() && xref.getMap().getMap().isTripletMap()) { - if (xref.getMap().getTo() != null) + SequenceI mappedTo = xref.getMap().getTo(); + if (mappedTo != null) { - SequenceI rsq = new Sequence(xref.getMap().getTo()); + /* + * dbref contains the sequence it maps to; add it to the + * results unless we have done so already (could happen if + * fetching xrefs for sequences which have xrefs in common) + * for example: UNIPROT {P0CE19, P0CE20} -> EMBL {J03321, X06707} + */ + found = true; + /* + * problem: matcher.findIdMatch() is lenient - returns a sequence + * with a dbref to the search arg e.g. ENST for ENSP - wrong + * but findInDataset() matches ENSP when looking for Uniprot... + */ + SequenceI matchInDataset = findInDataset(xref); + if (matchInDataset != null && xref.getMap().getTo() != null + && matchInDataset != xref.getMap().getTo()) + { + System.err + .println("Implementation problem (reopen JAL-2154): CrossRef.findInDataset seems to have recovered a different sequence than the one explicitly mapped for xref." + + "Found:" + + matchInDataset + + "\nExpected:" + + xref.getMap().getTo() + + "\nFor xref:" + + xref); + } + /*matcher.findIdMatch(mappedTo);*/ + if (matchInDataset != null) + { + if (!rseqs.contains(matchInDataset)) + { + rseqs.add(matchInDataset); + // need to try harder to only add unique mappings + if (xref.getMap().getMap().isTripletMap() + && dataset.getMapping(seq, matchInDataset) == null + && cf.getMappingBetween(seq, matchInDataset) == null) + { + // materialise a mapping for highlighting between these sequences + if (fromDna) + { + cf.addMap(dss, matchInDataset, xref.getMap().getMap(), xref.getMap().getMappedFromId()); + } else { + cf.addMap(matchInDataset, dss, xref.getMap().getMap().getInverse(), xref.getMap().getMappedFromId()); + } + } + } + refIterator.remove(); + continue; + } + // TODO: need to determine if this should be a deriveSequence + SequenceI rsq = new Sequence(mappedTo); rseqs.add(rsq); - if (xref.getMap().getMap().getFromRatio() != xref - .getMap().getMap().getToRatio()) + if (xref.getMap().getMap().isTripletMap()) { // get sense of map correct for adding to product alignment. - if (dna) + if (fromDna) { // map is from dna seq to a protein product - cf.addMap(dss, rsq, xref.getMap().getMap()); + cf.addMap(dss, rsq, xref.getMap().getMap(), xref.getMap() + .getMappedFromId()); } else { // map should be from protein seq to its coding dna - cf.addMap(rsq, dss, xref.getMap().getMap().getInverse()); + cf.addMap(rsq, dss, xref.getMap().getMap().getInverse(), + xref.getMap().getMappedFromId()); } } - found = true; } } + if (!found) { - // do a bit more work - search for sequences with references matching - // xrefs on this sequence. - if (dataset != null) + SequenceI matchedSeq = matcher.findIdMatch(xref.getSource() + "|" + + xref.getAccessionId()); + // if there was a match, check it's at least the right type of + // molecule! + if (matchedSeq != null && matchedSeq.isProtein() == fromDna) { - found |= searchDataset(dss, xref, dataset, rseqs, cf, false, - !dna); - if (found) + if (constructMapping(seq, matchedSeq, xref, cf, fromDna)) { - xrfs[r] = null; // we've recovered seqs for this one. + found = true; } } } + + if (!found) + { + // do a bit more work - search for sequences with references matching + // xrefs on this sequence. + found = searchDataset(fromDna, dss, xref, rseqs, cf, false); + } + if (found) + { + refIterator.remove(); + } } - if (!found) + + /* + * fetch from source database any dbrefs we haven't resolved up to here + */ + if (!sourceRefs.isEmpty()) { - if (xrfs != null && xrfs.length > 0) + retrieveCrossRef(sourceRefs, seq, xrfs, fromDna, cf); + } + } + + Alignment ral = null; + if (rseqs.size() > 0) + { + ral = new Alignment(rseqs.toArray(new SequenceI[rseqs.size()])); + if (!cf.isEmpty()) + { + dataset.addCodonFrame(cf); + } + } + return ral; + } + + private void retrieveCrossRef(List sourceRefs, SequenceI seq, + DBRefEntry[] xrfs, boolean fromDna, AlignedCodonFrame cf) + { + ASequenceFetcher sftch = SequenceFetcherFactory.getSequenceFetcher(); + SequenceI[] retrieved = null; + SequenceI dss = seq.getDatasetSequence() == null ? seq : seq + .getDatasetSequence(); + // first filter in case we are retrieving crossrefs that have already been + // retrieved. this happens for cases where a database record doesn't yield + // protein products for CDS + DBRefEntry[] dbrSourceSet = sourceRefs.toArray(new DBRefEntry[0]); + for (SequenceI sq : dataset.getSequences()) + { + boolean dupeFound = false; + // !fromDna means we are looking only for nucleotide sequences, not + // protein + if (sq.isProtein() == fromDna) + { + for (DBRefEntry dbr : sq.getPrimaryDBRefs()) { - // Try and get the sequence reference... - /* - * Ideal world - we ask for a sequence fetcher implementation here if - * (jalview.io.RunTimeEnvironment.getSequenceFetcher()) ( - */ - ASequenceFetcher sftch = new SequenceFetcher(); - SequenceI[] retrieved = null; - int l = xrfs.length; - for (int r = 0; r < xrfs.length; r++) + for (DBRefEntry found : DBRefUtils.searchRefs(dbrSourceSet, dbr)) { - // filter out any irrelevant or irretrievable references - if (xrfs[r] == null - || ((source != null && !source.equals(xrfs[r] - .getSource())) || !sftch.isFetchable(xrfs[r] - .getSource()))) - { - l--; - xrfs[r] = null; - } + sourceRefs.remove(found); + dupeFound = true; } - if (l > 0) - { - // System.out - // .println("Attempting to retrieve cross referenced sequences."); - DBRefEntry[] t = new DBRefEntry[l]; - l = 0; - for (int r = 0; r < xrfs.length; r++) - { - if (xrfs[r] != null) - { - t[l++] = xrfs[r]; - } - } - xrfs = t; - try - { - retrieved = sftch.getSequences(xrfs, !dna); - // problem here is we don't know which of xrfs resulted in which - // retrieved element - } catch (Exception e) - { - System.err - .println("Problem whilst retrieving cross references for Sequence : " - + seq.getName()); - e.printStackTrace(); - } + } + } + if (dupeFound) + { + dbrSourceSet = sourceRefs.toArray(new DBRefEntry[0]); + } + } + if (sourceRefs.size() == 0) + { + // no more work to do! We already had all requested sequence records in + // the dataset. + return; + } + try + { + retrieved = sftch.getSequences(sourceRefs, !fromDna); + } catch (Exception e) + { + System.err + .println("Problem whilst retrieving cross references for Sequence : " + + seq.getName()); + e.printStackTrace(); + } - if (retrieved != null) + if (retrieved != null) + { + updateDbrefMappings(seq, xrfs, retrieved, cf, fromDna); + for (SequenceI retrievedSequence : retrieved) + { + // dataset gets contaminated ccwith non-ds sequences. why ??! + // try: Ensembl -> Nuc->Ensembl, Nuc->Uniprot-->Protein->EMBL-> + SequenceI retrievedDss = retrievedSequence.getDatasetSequence() == null ? retrievedSequence + : retrievedSequence.getDatasetSequence(); + DBRefEntry[] dbr = retrievedSequence.getDBRefs(); + if (dbr != null) + { + for (DBRefEntry dbref : dbr) + { + // find any entry where we should put in the sequence being + // cross-referenced into the map + Mapping map = dbref.getMap(); + if (map != null) { - updateDbrefMappings(dna, seq, xrfs, retrieved, cf); - - SequenceIdMatcher matcher = new SequenceIdMatcher( - dataset.getSequences()); - List copiedFeatures = new ArrayList(); - CrossRef me = new CrossRef(); - for (int rs = 0; rs < retrieved.length; rs++) + if (map.getTo() != null && map.getMap() != null) { - // TODO: examine each sequence for 'redundancy' - DBRefEntry[] dbr = retrieved[rs].getDBRefs(); - if (dbr != null && dbr.length > 0) + // TODO findInDataset requires exact sequence match but + // 'congruent' test is only for the mapped part + // maybe not a problem in practice since only ENA provide a + // mapping and it is to the full protein translation of CDS + SequenceI matched = findInDataset(dbref); + // matcher.findIdMatch(map.getTo()); + if (matched != null) + { + /* + * already got an xref to this sequence; update this + * map to point to the same sequence, and add + * any new dbrefs to it + */ + DBRefEntry[] toRefs = map.getTo().getDBRefs(); + if (toRefs != null) + { + for (DBRefEntry ref : toRefs) + { + matched.addDBRef(ref); // add or update mapping + } + } + map.setTo(matched); + } + else { - for (int di = 0; di < dbr.length; di++) + if (dataset.findIndex(map.getTo()) == -1) { - // find any entry where we should put in the sequence being - // cross-referenced into the map - Mapping map = dbr[di].getMap(); - if (map != null) + dataset.addSequence(map.getTo()); + matcher.add(map.getTo()); + } + } + try + { + // compare ms with dss and replace with dss in mapping + // if map is congruent + SequenceI ms = map.getTo(); + int sf = map.getMap().getToLowest(); + int st = map.getMap().getToHighest(); + SequenceI mappedrg = ms.getSubSequence(sf, st); + // SequenceI loc = dss.getSubSequence(sf, st); + if (mappedrg.getLength() > 0 + && ms.getSequenceAsString().equals( + dss.getSequenceAsString())) + // && mappedrg.getSequenceAsString().equals( + // loc.getSequenceAsString())) + { + String msg = "Mapping updated from " + ms.getName() + + " to retrieved crossreference " + + dss.getName(); + System.out.println(msg); + map.setTo(dss); + + /* + * give the reverse reference the inverse mapping + * (if it doesn't have one already) + */ + setReverseMapping(dss, dbref, cf); + + /* + * copy sequence features as well, avoiding + * duplication (e.g. same variation from two + * transcripts) + */ + SequenceFeature[] sfs = ms.getSequenceFeatures(); + if (sfs != null) { - if (map.getTo() != null && map.getMap() != null) + for (SequenceFeature feat : sfs) { - SequenceI matched = matcher - .findIdMatch(map.getTo()); - if (matched != null) - { - /* - * already got an xref to this sequence; update this - * map to point to the same sequence, and add - * any new dbrefs to it - */ - for (DBRefEntry ref : map.getTo().getDBRefs()) - { - matched.addDBRef(ref); // add or update mapping - } - map.setTo(matched); - } - else + /* + * make a flyweight feature object which ignores Parent + * attribute in equality test; this avoids creating many + * otherwise duplicate exon features on genomic sequence + */ + SequenceFeature newFeature = new SequenceFeature( + feat) { - matcher.add(map.getTo()); - } - try - { - // compare ms with dss and replace with dss in mapping - // if map is congruent - SequenceI ms = map.getTo(); - int sf = map.getMap().getToLowest(); - int st = map.getMap().getToHighest(); - SequenceI mappedrg = ms.getSubSequence(sf, st); - // SequenceI loc = dss.getSubSequence(sf, st); - if (mappedrg.getLength() > 0 - && ms.getSequenceAsString().equals( - dss.getSequenceAsString())) - // && mappedrg.getSequenceAsString().equals( - // loc.getSequenceAsString())) - { - String msg = "Mapping updated from " - + ms.getName() - + " to retrieved crossreference " - + dss.getName(); - System.out.println(msg); - // method to update all refs of existing To on - // retrieved sequence with dss and merge any props - // on To onto dss. - map.setTo(dss); - /* - * copy sequence features as well, avoiding - * duplication (e.g. same variation from 2 - * transcripts) - */ - SequenceFeature[] sfs = ms - .getSequenceFeatures(); - if (sfs != null) - { - for (SequenceFeature feat : sfs) - { - /* - * we override SequenceFeature.equals here (but - * not elsewhere) to ignore Parent attribute - * TODO not quite working yet! - */ - if (!copiedFeatures - .contains(me.new MySequenceFeature( - feat))) - { - dss.addSequenceFeature(feat); - copiedFeatures.add(feat); - } - } - } - cf.addMap(retrieved[rs].getDatasetSequence(), - dss, map.getMap()); - } - else + @Override + public boolean equals(Object o) { - cf.addMap(retrieved[rs].getDatasetSequence(), - map.getTo(), map.getMap()); + return super.equals(o, true); } - } catch (Exception e) - { - System.err - .println("Exception when consolidating Mapped sequence set..."); - e.printStackTrace(System.err); - } + }; + dss.addSequenceFeature(newFeature); } } } + cf.addMap(retrievedDss, map.getTo(), map.getMap()); + } catch (Exception e) + { + System.err + .println("Exception when consolidating Mapped sequence set..."); + e.printStackTrace(System.err); } - retrieved[rs].updatePDBIds(); - rseqs.add(retrieved[rs]); } } } } + retrievedSequence.updatePDBIds(); + rseqs.add(retrievedDss); + if (dataset.findIndex(retrievedDss) == -1) + { + dataset.addSequence(retrievedDss); + matcher.add(retrievedDss); + } } } + } + /** + * Sets the inverse sequence mapping in the corresponding dbref of the mapped + * to sequence (if any). This is used after fetching a cross-referenced + * sequence, if the fetched sequence has a mapping to the original sequence, + * to set the mapping in the original sequence's dbref. + * + * @param mapFrom + * the sequence mapped from + * @param dbref + * @param mappings + */ + void setReverseMapping(SequenceI mapFrom, DBRefEntry dbref, + AlignedCodonFrame mappings) + { + SequenceI mapTo = dbref.getMap().getTo(); + if (mapTo == null) + { + return; + } + DBRefEntry[] dbrefs = mapTo.getDBRefs(); + if (dbrefs == null) + { + return; + } + for (DBRefEntry toRef : dbrefs) + { + if (toRef.hasMap() && mapFrom == toRef.getMap().getTo()) + { + /* + * found the reverse dbref; update its mapping if null + */ + if (toRef.getMap().getMap() == null) + { + MapList inverse = dbref.getMap().getMap().getInverse(); + toRef.getMap().setMap(inverse); + mappings.addMap(mapTo, mapFrom, inverse); + } + } + } + } - Alignment ral = null; - if (rseqs.size() > 0) + /** + * Returns the first identical sequence in the dataset if any, else null + * + * @param xref + * @return + */ + SequenceI findInDataset(DBRefEntry xref) + { + if (xref == null || !xref.hasMap() || xref.getMap().getTo() == null) { - ral = new Alignment(rseqs.toArray(new SequenceI[rseqs.size()])); - if (cf != null && !cf.isEmpty()) + return null; + } + SequenceI mapsTo = xref.getMap().getTo(); + String name = xref.getAccessionId(); + String name2 = xref.getSource() + "|" + name; + SequenceI dss = mapsTo.getDatasetSequence() == null ? mapsTo : mapsTo + .getDatasetSequence(); + for (SequenceI seq : dataset.getSequences()) + { + /* + * clumsy alternative to using SequenceIdMatcher which currently + * returns sequences with a dbref to the matched accession id + * which we don't want + */ + if (name.equals(seq.getName()) || seq.getName().startsWith(name2)) { - ral.addCodonFrame(cf); + if (sameSequence(seq, dss)) + { + return seq; + } } } - return ral; + return null; + } + + /** + * Answers true if seq1 and seq2 contain exactly the same characters (ignoring + * case), else false. This method compares the lengths, then each character in + * turn, in order to 'fail fast'. For case-sensitive comparison, it would be + * possible to use Arrays.equals(seq1.getSequence(), seq2.getSequence()). + * + * @param seq1 + * @param seq2 + * @return + */ + // TODO move to Sequence / SequenceI + static boolean sameSequence(SequenceI seq1, SequenceI seq2) + { + if (seq1 == seq2) + { + return true; + } + if (seq1 == null || seq2 == null) + { + return false; + } + char[] c1 = seq1.getSequence(); + char[] c2 = seq2.getSequence(); + if (c1.length != c2.length) + { + return false; + } + for (int i = 0; i < c1.length; i++) + { + int diff = c1[i] - c2[i]; + /* + * same char or differ in case only ('a'-'A' == 32) + */ + if (diff != 0 && diff != 32 && diff != -32) + { + return false; + } + } + return true; } /** @@ -487,78 +682,146 @@ public class CrossRef * retrieved sequence if found, and adds any new mappings to the * AlignedCodonFrame * - * @param dna * @param mapFrom * @param xrefs * @param retrieved * @param acf */ - static void updateDbrefMappings(boolean dna, SequenceI mapFrom, - DBRefEntry[] xrefs, SequenceI[] retrieved, AlignedCodonFrame acf) + void updateDbrefMappings(SequenceI mapFrom, DBRefEntry[] xrefs, + SequenceI[] retrieved, AlignedCodonFrame acf, boolean fromDna) { - SequenceIdMatcher matcher = new SequenceIdMatcher(retrieved); + SequenceIdMatcher idMatcher = new SequenceIdMatcher(retrieved); for (DBRefEntry xref : xrefs) { if (!xref.hasMap()) { String targetSeqName = xref.getSource() + "|" + xref.getAccessionId(); - SequenceI[] matches = matcher.findAllIdMatches(targetSeqName); + SequenceI[] matches = idMatcher.findAllIdMatches(targetSeqName); if (matches == null) { return; } for (SequenceI seq : matches) { - MapList mapping = null; - if (dna) - { - mapping = AlignmentUtils.mapCdnaToProtein(seq, mapFrom); - } - else - { - mapping = AlignmentUtils.mapCdnaToProtein(mapFrom, seq); - if (mapping != null) - { - mapping = mapping.getInverse(); - } - } - if (mapping != null) - { - xref.setMap(new Mapping(seq, mapping)); - if (dna) - { - AlignmentUtils.computeProteinFeatures(mapFrom, seq, mapping); - } - if (dna) - { - acf.addMap(mapFrom, seq, mapping); - } - else - { - acf.addMap(seq, mapFrom, mapping.getInverse()); - } - continue; - } + constructMapping(mapFrom, seq, xref, acf, fromDna); } } } } /** + * Tries to make a mapping between sequences. If successful, adds the mapping + * to the dbref and the mappings collection and answers true, otherwise + * answers false. The following methods of making are mapping are tried in + * turn: + *
          + *
        • if 'mapTo' holds a mapping to 'mapFrom', take the inverse; this is, for + * example, the case after fetching EMBL cross-references for a Uniprot + * sequence
        • + *
        • else check if the dna translates exactly to the protein (give or take + * start and stop codons>
        • + *
        • else try to map based on CDS features on the dna sequence
        • + *
        + * + * @param mapFrom + * @param mapTo + * @param xref + * @param mappings + * @return + */ + boolean constructMapping(SequenceI mapFrom, SequenceI mapTo, + DBRefEntry xref, AlignedCodonFrame mappings, boolean fromDna) + { + MapList mapping = null; + SequenceI dsmapFrom = mapFrom.getDatasetSequence() == null ? mapFrom + : mapFrom.getDatasetSequence(); + SequenceI dsmapTo = mapTo.getDatasetSequence() == null ? mapTo + : mapTo.getDatasetSequence(); + /* + * look for a reverse mapping, if found make its inverse. + * Note - we do this on dataset sequences only. + */ + if (dsmapTo.getDBRefs() != null) + { + for (DBRefEntry dbref : dsmapTo.getDBRefs()) + { + String name = dbref.getSource() + "|" + dbref.getAccessionId(); + if (dbref.hasMap() && dsmapFrom.getName().startsWith(name)) + { + /* + * looks like we've found a map from 'mapTo' to 'mapFrom' + * - invert it to make the mapping the other way + */ + MapList reverse = dbref.getMap().getMap().getInverse(); + xref.setMap(new Mapping(dsmapTo, reverse)); + mappings.addMap(mapFrom, dsmapTo, reverse); + return true; + } + } + } + + if (fromDna) + { + mapping = AlignmentUtils.mapCdnaToProtein(mapTo, mapFrom); + } + else + { + mapping = AlignmentUtils.mapCdnaToProtein(mapFrom, mapTo); + if (mapping != null) + { + mapping = mapping.getInverse(); + } + } + if (mapping == null) + { + return false; + } + xref.setMap(new Mapping(mapTo, mapping)); + + /* + * and add a reverse DbRef with the inverse mapping + */ + if (mapFrom.getDatasetSequence() != null && false) + // && mapFrom.getDatasetSequence().getSourceDBRef() != null) + { + // possible need to search primary references... except, why doesn't xref + // == getSourceDBRef ?? + // DBRefEntry dbref = new DBRefEntry(mapFrom.getDatasetSequence() + // .getSourceDBRef()); + // dbref.setMap(new Mapping(mapFrom.getDatasetSequence(), mapping + // .getInverse())); + // mapTo.addDBRef(dbref); + } + + if (fromDna) + { + AlignmentUtils.computeProteinFeatures(mapFrom, mapTo, mapping); + mappings.addMap(mapFrom, mapTo, mapping); + } + else + { + mappings.addMap(mapTo, mapFrom, mapping.getInverse()); + } + + return true; + } + + /** * find references to lrfs in the cross-reference set of each sequence in * dataset (that is not equal to sequenceI) Identifies matching DBRefEntry * based on source and accession string only - Map and Version are nulled. * + * @param fromDna + * - true if context was searching from Dna sequences, false if + * context was searching from Protein sequences * @param sequenceI * @param lrfs - * @param dataset - * @param rseqs + * @param foundSeqs * @return true if matches were found. */ - private static boolean searchDatasetXrefs(SequenceI sequenceI, - boolean dna, DBRefEntry[] lrfs, AlignmentI dataset, - List rseqs, AlignedCodonFrame cf) + private boolean searchDatasetXrefs(boolean fromDna, SequenceI sequenceI, + DBRefEntry[] lrfs, List foundSeqs, AlignedCodonFrame cf) { boolean found = false; if (lrfs == null) @@ -571,50 +834,44 @@ public class CrossRef // add in wildcards xref.setVersion(null); xref.setMap(null); - found = searchDataset(sequenceI, xref, dataset, rseqs, cf, false, dna); + found |= searchDataset(fromDna, sequenceI, xref, foundSeqs, cf, false); } return found; } /** - * search a given sequence dataset for references matching cross-references to - * the given sequence + * Searches dataset for DBRefEntrys matching the given one (xrf) and adds the + * associated sequence to rseqs * - * @param sequenceI - * @param xrf - * @param dataset - * @param rseqs - * set of unique sequences - * @param cf - * @return true if one or more unique sequences were found and added - */ - public static boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf, - AlignmentI dataset, List rseqs, AlignedCodonFrame cf) - { - return searchDataset(sequenceI, xrf, dataset, rseqs, cf, true, false); - } - - /** - * TODO: generalise to different protein classifications Search dataset for - * DBRefEntrys matching the given one (xrf) and add the associated sequence to - * rseq. - * - * @param sequenceI + * @param fromDna + * true if context was searching for refs *from* dna sequence, false + * if context was searching for refs *from* protein sequence + * @param fromSeq + * a sequence to ignore (start point of search) * @param xrf - * @param dataset - * @param rseqs + * a cross-reference to try to match + * @param foundSeqs + * result list to add to + * @param mappings + * a set of sequence mappings to add to * @param direct - * - search all references or only subset - * @param dna - * search dna or protein xrefs (if direct=false) + * - indicates the type of relationship between returned sequences, + * xrf, and sequenceI that is required. + *
          + *
        • direct implies xrf is a primary reference for sequenceI AND + * the sequences to be located (eg a uniprot ID for a protein + * sequence, and a uniprot ref on a transcript sequence).
        • + *
        • indirect means xrf is a cross reference with respect to + * sequenceI or all the returned sequences (eg a genomic reference + * associated with a locus and one or more transcripts)
        • + *
        * @return true if relationship found and sequence added. */ - public static boolean searchDataset(SequenceI sequenceI, DBRefEntry xrf, - AlignmentI dataset, List rseqs, AlignedCodonFrame cf, - boolean direct, boolean dna) + boolean searchDataset(boolean fromDna, SequenceI fromSeq, DBRefEntry xrf, + List foundSeqs, AlignedCodonFrame mappings, + boolean direct) { boolean found = false; - SequenceI[] typer = new SequenceI[1]; if (dataset == null) { return false; @@ -634,107 +891,85 @@ public class CrossRef if (nxt.getDatasetSequence() != null) { System.err - .println("Implementation warning: getProducts passed a dataset alignment without dataset sequences in it!"); + .println("Implementation warning: CrossRef initialised with a dataset alignment with non-dataset sequences in it! (" + + nxt.getDisplayId(true) + + " has ds reference " + + nxt.getDatasetSequence().getDisplayId(true) + + ")"); } - if (nxt != sequenceI && nxt != sequenceI.getDatasetSequence()) + if (nxt == fromSeq || nxt == fromSeq.getDatasetSequence()) { - // check if this is the correct sequence type + continue; + } + /* + * only look at same molecule type if 'direct', or + * complementary type if !direct + */ + { + boolean isDna = !nxt.isProtein(); + if (direct ? (isDna != fromDna) : (isDna == fromDna)) { - typer[0] = nxt; - boolean isDna = jalview.util.Comparison.isNucleotide(typer); - if ((direct && isDna == dna) || (!direct && isDna != dna)) - { - // skip this sequence because it is same molecule type - continue; - } + // skip this sequence because it is wrong molecule type + continue; } + } - // look for direct or indirect references in common - DBRefEntry[] poss = nxt.getDBRefs(), cands = null; - if (direct) - { - cands = jalview.util.DBRefUtils.searchRefs(poss, xrf); - } - else + // look for direct or indirect references in common + DBRefEntry[] poss = nxt.getDBRefs(); + List cands = null; + + // todo: indirect specifies we select either direct references to nxt + // that match xrf which is indirect to sequenceI, or indirect + // references to nxt that match xrf which is direct to sequenceI + cands = DBRefUtils.searchRefs(poss, xrf); + // else + // { + // poss = DBRefUtils.selectDbRefs(nxt.isProtein()!fromDna, poss); + // cands = DBRefUtils.searchRefs(poss, xrf); + // } + if (!cands.isEmpty()) + { + if (foundSeqs.contains(nxt)) { - poss = CrossRef.findXDbRefs(dna, poss); // - cands = jalview.util.DBRefUtils.searchRefs(poss, xrf); + continue; } - if (cands != null) + found = true; + foundSeqs.add(nxt); + if (mappings != null && !direct) { - if (!rseqs.contains(nxt)) + /* + * if the matched sequence has mapped dbrefs to + * protein product / cdna, add equivalent mappings to + * our source sequence + */ + for (DBRefEntry candidate : cands) { - rseqs.add(nxt); - boolean foundmap = cf != null; - // don't search if we aren't given a codon map object - for (int r = 0; foundmap && r < cands.length; r++) + Mapping mapping = candidate.getMap(); + if (mapping != null) { - if (cands[r].hasMap()) + MapList map = mapping.getMap(); + if (mapping.getTo() != null + && map.getFromRatio() != map.getToRatio()) { - if (cands[r].getMap().getTo() != null - && cands[r].getMap().getMap().getFromRatio() != cands[r] - .getMap().getMap().getToRatio()) + /* + * add a mapping, as from dna to peptide sequence + */ + if (map.getFromRatio() == 3) { - foundmap = true; - // get sense of map correct for adding to product - // alignment. - if (dna) - { - // map is from dna seq to a protein product - cf.addMap(sequenceI, nxt, cands[r].getMap() - .getMap()); - } - else - { - // map should be from protein seq to its coding dna - cf.addMap(nxt, sequenceI, cands[r].getMap() - .getMap().getInverse()); - } + mappings.addMap(nxt, fromSeq, map); + } + else + { + mappings.addMap(nxt, fromSeq, map.getInverse()); } } } - // TODO: add mapping between sequences if necessary - found = true; } } - } } } } return found; } - - /** - * precalculate different products that can be found for seqs in dataset and - * return them. - * - * @param dna - * @param seqs - * @param dataset - * @param fake - * - don't actually build lists - just get types - * @return public static Object[] buildXProductsList(boolean dna, SequenceI[] - * seqs, AlignmentI dataset, boolean fake) { String types[] = - * jalview.analysis.CrossRef.findSequenceXrefTypes( dna, seqs, - * dataset); if (types != null) { System.out.println("Xref Types for: - * "+(dna ? "dna" : "prot")); for (int t = 0; t < types.length; t++) { - * System.out.println("Type: " + types[t]); SequenceI[] prod = - * jalview.analysis.CrossRef.findXrefSequences(seqs, dna, types[t]); - * System.out.println("Found " + ((prod == null) ? "no" : "" + - * prod.length) + " products"); if (prod!=null) { for (int p=0; - * p cluster; SequenceI[] sequence; @@ -68,7 +68,7 @@ public class NJTree float rj; - Vector groups = new Vector(); + Vector groups = new Vector(); SequenceNode maxdist; @@ -80,7 +80,7 @@ public class NJTree int ycount; - Vector node; + Vector node; String type; @@ -88,8 +88,6 @@ public class NJTree Object found = null; - Object leaves = null; - boolean hasDistances = true; // normal case for jalview trees boolean hasBootstrap = false; // normal case for jalview trees @@ -151,8 +149,7 @@ public class NJTree SequenceIdMatcher algnIds = new SequenceIdMatcher(seqs); - Vector leaves = new Vector(); - findLeaves(top, leaves); + Vector leaves = findLeaves(top); int i = 0; int namesleft = seqs.length; @@ -160,11 +157,11 @@ public class NJTree SequenceNode j; SequenceI nam; String realnam; - Vector one2many = new Vector(); + Vector one2many = new Vector(); int countOne2Many = 0; while (i < leaves.size()) { - j = (SequenceNode) leaves.elementAt(i++); + j = leaves.elementAt(i++); realnam = j.getName(); nam = null; @@ -221,7 +218,7 @@ public class NJTree String pwtype, ScoreModelI sm, int start, int end) { this.sequence = sequence; - this.node = new Vector(); + this.node = new Vector(); this.type = type; this.pwtype = pwtype; if (seqData != null) @@ -282,6 +279,7 @@ public class NJTree * * @return Newick File with all tree data available */ + @Override public String toString() { jalview.io.NewickFile fout = new jalview.io.NewickFile(getTopNode()); @@ -299,8 +297,7 @@ public class NJTree */ public void UpdatePlaceHolders(List list) { - Vector leaves = new Vector(); - findLeaves(top, leaves); + Vector leaves = findLeaves(top); int sz = leaves.size(); SequenceIdMatcher seqmatcher = null; @@ -308,7 +305,7 @@ public class NJTree while (i < sz) { - SequenceNode leaf = (SequenceNode) leaves.elementAt(i++); + SequenceNode leaf = leaves.elementAt(i++); if (list.contains(leaf.element())) { @@ -369,12 +366,12 @@ public class NJTree { @Override - public void transform(BinaryNode node) + public void transform(BinaryNode nd) { - Object el = node.element(); + Object el = nd.element(); if (el != null && el instanceof SequenceI) { - node.setName(((SequenceI) el).getName()); + nd.setName(((SequenceI) el).getName()); } } }); @@ -428,7 +425,7 @@ public class NJTree } joinClusters(one, two); - top = (SequenceNode) (node.elementAt(one)); + top = (node.elementAt(one)); reCount(top); findHeight(top); @@ -449,19 +446,19 @@ public class NJTree { float dist = distance[i][j]; - int noi = ((Cluster) cluster.elementAt(i)).value.length; - int noj = ((Cluster) cluster.elementAt(j)).value.length; + int noi = cluster.elementAt(i).value.length; + int noj = cluster.elementAt(j).value.length; int[] value = new int[noi + noj]; for (int ii = 0; ii < noi; ii++) { - value[ii] = ((Cluster) cluster.elementAt(i)).value[ii]; + value[ii] = cluster.elementAt(i).value[ii]; } for (int ii = noi; ii < (noi + noj); ii++) { - value[ii] = ((Cluster) cluster.elementAt(j)).value[ii - noi]; + value[ii] = cluster.elementAt(j).value[ii - noi]; } Cluster c = new Cluster(value); @@ -480,11 +477,11 @@ public class NJTree SequenceNode sn = new SequenceNode(); - sn.setLeft((SequenceNode) (node.elementAt(i))); - sn.setRight((SequenceNode) (node.elementAt(j))); + sn.setLeft((node.elementAt(i))); + sn.setRight((node.elementAt(j))); - SequenceNode tmpi = (SequenceNode) (node.elementAt(i)); - SequenceNode tmpj = (SequenceNode) (node.elementAt(j)); + SequenceNode tmpi = (node.elementAt(i)); + SequenceNode tmpj = (node.elementAt(j)); if (type.equals("NJ")) { @@ -576,8 +573,8 @@ public class NJTree */ public void findClusterDistance(int i, int j) { - int noi = ((Cluster) cluster.elementAt(i)).value.length; - int noj = ((Cluster) cluster.elementAt(j)).value.length; + int noi = cluster.elementAt(i).value.length; + int noj = cluster.elementAt(j).value.length; // New distances from cluster to others float[] newdist = new float[noseqs]; @@ -733,7 +730,7 @@ public class NJTree public float[][] findDistances(ScoreModelI _pwmatrix) { - float[][] distance = new float[noseqs][noseqs]; + float[][] dist = new float[noseqs][noseqs]; if (_pwmatrix == null) { // Resolve substitution model @@ -743,8 +740,8 @@ public class NJTree _pwmatrix = ResidueProperties.getScoreMatrix("BLOSUM62"); } } - distance = _pwmatrix.findDistances(seqData); - return distance; + dist = _pwmatrix.findDistances(seqData); + return dist; } @@ -753,7 +750,7 @@ public class NJTree */ public void makeLeaves() { - cluster = new Vector(); + cluster = new Vector(); for (int i = 0; i < noseqs; i++) { @@ -772,26 +769,42 @@ public class NJTree } /** + * Search for leaf nodes below (or at) the given node + * + * @param nd + * root node to search from + * + * @return + */ + public Vector findLeaves(SequenceNode nd) + { + Vector leaves = new Vector(); + findLeaves(nd, leaves); + return leaves; + } + + /** * Search for leaf nodes. * - * @param node + * @param nd * root node to search from * @param leaves * Vector of leaves to add leaf node objects too. * * @return Vector of leaf nodes on binary tree */ - public Vector findLeaves(SequenceNode node, Vector leaves) + Vector findLeaves(SequenceNode nd, + Vector leaves) { - if (node == null) + if (nd == null) { return leaves; } - if ((node.left() == null) && (node.right() == null)) // Interior node + if ((nd.left() == null) && (nd.right() == null)) // Interior node // detection { - leaves.addElement(node); + leaves.addElement(nd); return leaves; } @@ -801,8 +814,8 @@ public class NJTree * TODO: Identify internal nodes... if (node.isSequenceLabel()) { * leaves.addElement(node); } */ - findLeaves((SequenceNode) node.left(), leaves); - findLeaves((SequenceNode) node.right(), leaves); + findLeaves((SequenceNode) nd.left(), leaves); + findLeaves((SequenceNode) nd.right(), leaves); } return leaves; @@ -811,16 +824,16 @@ public class NJTree /** * Find the leaf node with a particular ycount * - * @param node + * @param nd * initial point on tree to search from * @param count * value to search for * * @return null or the node with ycound=count */ - public Object findLeaf(SequenceNode node, int count) + public Object findLeaf(SequenceNode nd, int count) { - found = _findLeaf(node, count); + found = _findLeaf(nd, count); return found; } @@ -828,23 +841,23 @@ public class NJTree /* * #see findLeaf(SequenceNode node, count) */ - public Object _findLeaf(SequenceNode node, int count) + public Object _findLeaf(SequenceNode nd, int count) { - if (node == null) + if (nd == null) { return null; } - if (node.ycount == count) + if (nd.ycount == count) { - found = node.element(); + found = nd.element(); return found; } else { - _findLeaf((SequenceNode) node.left(), count); - _findLeaf((SequenceNode) node.right(), count); + _findLeaf((SequenceNode) nd.left(), count); + _findLeaf((SequenceNode) nd.right(), count); } return found; @@ -853,58 +866,58 @@ public class NJTree /** * printNode is mainly for debugging purposes. * - * @param node + * @param nd * SequenceNode */ - public void printNode(SequenceNode node) + public void printNode(SequenceNode nd) { - if (node == null) + if (nd == null) { return; } - if ((node.left() == null) && (node.right() == null)) + if ((nd.left() == null) && (nd.right() == null)) { System.out - .println("Leaf = " + ((SequenceI) node.element()).getName()); - System.out.println("Dist " + node.dist); - System.out.println("Boot " + node.getBootstrap()); + .println("Leaf = " + ((SequenceI) nd.element()).getName()); + System.out.println("Dist " + nd.dist); + System.out.println("Boot " + nd.getBootstrap()); } else { - System.out.println("Dist " + node.dist); - printNode((SequenceNode) node.left()); - printNode((SequenceNode) node.right()); + System.out.println("Dist " + nd.dist); + printNode((SequenceNode) nd.left()); + printNode((SequenceNode) nd.right()); } } /** * DOCUMENT ME! * - * @param node + * @param nd * DOCUMENT ME! */ - public void findMaxDist(SequenceNode node) + public void findMaxDist(SequenceNode nd) { - if (node == null) + if (nd == null) { return; } - if ((node.left() == null) && (node.right() == null)) + if ((nd.left() == null) && (nd.right() == null)) { - float dist = node.dist; + float dist = nd.dist; if (dist > maxDistValue) { - maxdist = node; + maxdist = nd; maxDistValue = dist; } } else { - findMaxDist((SequenceNode) node.left()); - findMaxDist((SequenceNode) node.right()); + findMaxDist((SequenceNode) nd.left()); + findMaxDist((SequenceNode) nd.right()); } } @@ -913,7 +926,7 @@ public class NJTree * * @return DOCUMENT ME! */ - public Vector getGroups() + public Vector getGroups() { return groups; } @@ -931,51 +944,51 @@ public class NJTree /** * DOCUMENT ME! * - * @param node + * @param nd * DOCUMENT ME! * @param threshold * DOCUMENT ME! */ - public void groupNodes(SequenceNode node, float threshold) + public void groupNodes(SequenceNode nd, float threshold) { - if (node == null) + if (nd == null) { return; } - if ((node.height / maxheight) > threshold) + if ((nd.height / maxheight) > threshold) { - groups.addElement(node); + groups.addElement(nd); } else { - groupNodes((SequenceNode) node.left(), threshold); - groupNodes((SequenceNode) node.right(), threshold); + groupNodes((SequenceNode) nd.left(), threshold); + groupNodes((SequenceNode) nd.right(), threshold); } } /** * DOCUMENT ME! * - * @param node + * @param nd * DOCUMENT ME! * * @return DOCUMENT ME! */ - public float findHeight(SequenceNode node) + public float findHeight(SequenceNode nd) { - if (node == null) + if (nd == null) { return maxheight; } - if ((node.left() == null) && (node.right() == null)) + if ((nd.left() == null) && (nd.right() == null)) { - node.height = ((SequenceNode) node.parent()).height + node.dist; + nd.height = ((SequenceNode) nd.parent()).height + nd.dist; - if (node.height > maxheight) + if (nd.height > maxheight) { - return node.height; + return nd.height; } else { @@ -984,18 +997,18 @@ public class NJTree } else { - if (node.parent() != null) + if (nd.parent() != null) { - node.height = ((SequenceNode) node.parent()).height + node.dist; + nd.height = ((SequenceNode) nd.parent()).height + nd.dist; } else { maxheight = 0; - node.height = (float) 0.0; + nd.height = (float) 0.0; } - maxheight = findHeight((SequenceNode) (node.left())); - maxheight = findHeight((SequenceNode) (node.right())); + maxheight = findHeight((SequenceNode) (nd.left())); + maxheight = findHeight((SequenceNode) (nd.right())); } return maxheight; @@ -1078,43 +1091,43 @@ public class NJTree /** * DOCUMENT ME! * - * @param node + * @param nd * DOCUMENT ME! */ - public void printN(SequenceNode node) + public void printN(SequenceNode nd) { - if (node == null) + if (nd == null) { return; } - if ((node.left() != null) && (node.right() != null)) + if ((nd.left() != null) && (nd.right() != null)) { - printN((SequenceNode) node.left()); - printN((SequenceNode) node.right()); + printN((SequenceNode) nd.left()); + printN((SequenceNode) nd.right()); } else { System.out.println(" name = " - + ((SequenceI) node.element()).getName()); + + ((SequenceI) nd.element()).getName()); } - System.out.println(" dist = " + node.dist + " " + node.count + " " - + node.height); + System.out.println(" dist = " + nd.dist + " " + nd.count + " " + + nd.height); } /** * DOCUMENT ME! * - * @param node + * @param nd * DOCUMENT ME! */ - public void reCount(SequenceNode node) + public void reCount(SequenceNode nd) { ycount = 0; _lycount = 0; // _lylimit = this.node.size(); - _reCount(node); + _reCount(nd); } private long _lycount = 0, _lylimit = 0; @@ -1122,37 +1135,37 @@ public class NJTree /** * DOCUMENT ME! * - * @param node + * @param nd * DOCUMENT ME! */ - public void _reCount(SequenceNode node) + public void _reCount(SequenceNode nd) { // if (_lycount<_lylimit) // { // System.err.println("Warning: depth of _recount greater than number of nodes."); // } - if (node == null) + if (nd == null) { return; } _lycount++; - if ((node.left() != null) && (node.right() != null)) + if ((nd.left() != null) && (nd.right() != null)) { - _reCount((SequenceNode) node.left()); - _reCount((SequenceNode) node.right()); + _reCount((SequenceNode) nd.left()); + _reCount((SequenceNode) nd.right()); - SequenceNode l = (SequenceNode) node.left(); - SequenceNode r = (SequenceNode) node.right(); + SequenceNode l = (SequenceNode) nd.left(); + SequenceNode r = (SequenceNode) nd.right(); - node.count = l.count + r.count; - node.ycount = (l.ycount + r.ycount) / 2; + nd.count = l.count + r.count; + nd.ycount = (l.ycount + r.ycount) / 2; } else { - node.count = 1; - node.ycount = ycount++; + nd.count = 1; + nd.ycount = ycount++; } _lycount--; } @@ -1160,80 +1173,80 @@ public class NJTree /** * DOCUMENT ME! * - * @param node + * @param nd * DOCUMENT ME! */ - public void swapNodes(SequenceNode node) + public void swapNodes(SequenceNode nd) { - if (node == null) + if (nd == null) { return; } - SequenceNode tmp = (SequenceNode) node.left(); + SequenceNode tmp = (SequenceNode) nd.left(); - node.setLeft(node.right()); - node.setRight(tmp); + nd.setLeft(nd.right()); + nd.setRight(tmp); } /** * DOCUMENT ME! * - * @param node + * @param nd * DOCUMENT ME! * @param dir * DOCUMENT ME! */ - public void changeDirection(SequenceNode node, SequenceNode dir) + public void changeDirection(SequenceNode nd, SequenceNode dir) { - if (node == null) + if (nd == null) { return; } - if (node.parent() != top) + if (nd.parent() != top) { - changeDirection((SequenceNode) node.parent(), node); + changeDirection((SequenceNode) nd.parent(), nd); - SequenceNode tmp = (SequenceNode) node.parent(); + SequenceNode tmp = (SequenceNode) nd.parent(); - if (dir == node.left()) + if (dir == nd.left()) { - node.setParent(dir); - node.setLeft(tmp); + nd.setParent(dir); + nd.setLeft(tmp); } - else if (dir == node.right()) + else if (dir == nd.right()) { - node.setParent(dir); - node.setRight(tmp); + nd.setParent(dir); + nd.setRight(tmp); } } else { - if (dir == node.left()) + if (dir == nd.left()) { - node.setParent(node.left()); + nd.setParent(nd.left()); - if (top.left() == node) + if (top.left() == nd) { - node.setRight(top.right()); + nd.setRight(top.right()); } else { - node.setRight(top.left()); + nd.setRight(top.left()); } } else { - node.setParent(node.right()); + nd.setParent(nd.right()); - if (top.left() == node) + if (top.left() == nd) { - node.setLeft(top.right()); + nd.setLeft(top.right()); } else { - node.setLeft(top.left()); + nd.setLeft(top.left()); } } } @@ -1289,8 +1302,9 @@ public class NJTree */ public void applyToNodes(NodeTransformI nodeTransformI) { - for (Enumeration nodes = node.elements(); nodes.hasMoreElements(); nodeTransformI - .transform((BinaryNode) nodes.nextElement())) + for (Enumeration nodes = node.elements(); nodes + .hasMoreElements(); nodeTransformI + .transform(nodes.nextElement())) { ; } diff --git a/src/jalview/analysis/Rna.java b/src/jalview/analysis/Rna.java index e752126..e3d999a 100644 --- a/src/jalview/analysis/Rna.java +++ b/src/jalview/analysis/Rna.java @@ -31,82 +31,108 @@ import jalview.datamodel.SequenceFeature; import jalview.util.MessageManager; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; import java.util.Hashtable; +import java.util.List; import java.util.Stack; import java.util.Vector; public class Rna { - static Hashtable pairHash = new Hashtable(); - - private static final Character[] openingPars = { '(', '[', '{', '<', 'A', - 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; - - private static final Character[] closingPars = { ')', ']', '}', '>', 'a', - 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; - - private static HashSet openingParsSet = new HashSet( - Arrays.asList(openingPars)); - - private static HashSet closingParsSet = new HashSet( - Arrays.asList(closingPars)); + /** + * Answers true if the character is a valid open pair rna secondary structure + * symbol. Currently accepts A-Z, ([{< + * + * @param c + * @return + */ + public static boolean isOpeningParenthesis(char c) + { + return ('A' <= c && c <= 'Z' || c == '(' || c == '[' || c == '{' || c == '<'); + } - private static Hashtable closingToOpening = new Hashtable() - // Initializing final data structure + /** + * Answers true if the string is a valid open pair rna secondary structure + * symbol. Currently accepts A-Z, ([{< + * + * @param s + * @return + */ + public static boolean isOpeningParenthesis(String s) { - private static final long serialVersionUID = 1L; - { - for (int i = 0; i < openingPars.length; i++) - { - // System.out.println(closingPars[i] + "->" + openingPars[i]); - put(closingPars[i], openingPars[i]); - } - } - }; + return s != null && s.length() == 1 + && isOpeningParenthesis(s.charAt(0)); + } - private static boolean isOpeningParenthesis(char c) + /** + * Answers true if the character is a valid close pair rna secondary structure + * symbol. Currently accepts a-z, )]}> + * + * @param c + * @return + */ + public static boolean isClosingParenthesis(char c) { - return openingParsSet.contains(c); + return ('a' <= c && c <= 'z' || c == ')' || c == ']' || c == '}' || c == '>'); } - private static boolean isClosingParenthesis(char c) + /** + * Answers true if the string is a valid close pair rna secondary structure + * symbol. Currently accepts a-z, )]}> + * + * @param s + * @return + */ + public static boolean isClosingParenthesis(String s) { - return closingParsSet.contains(c); + return s != null && s.length() == 1 + && isClosingParenthesis(s.charAt(0)); } - private static char matchingOpeningParenthesis(char closingParenthesis) - throws WUSSParseException + /** + * Returns the matching open pair symbol for the given closing symbol. + * Currently returns A-Z for a-z, or ([{< for )]}>, or the input symbol if it + * is not a valid closing symbol. + * + * @param c + * @return + */ + public static char getMatchingOpeningParenthesis(char c) { - if (!isClosingParenthesis(closingParenthesis)) + if ('a' <= c && c <= 'z') { - throw new WUSSParseException( - MessageManager.formatMessage( - "exception.querying_matching_opening_parenthesis_for_non_closing_parenthesis", - new String[] { new StringBuffer(closingParenthesis) - .toString() }), -1); + return (char) (c + 'A' - 'a'); + } + switch (c) + { + case ')': + return '('; + case ']': + return '['; + case '}': + return '{'; + case '>': + return '<'; + default: + return c; } - - return closingToOpening.get(closingParenthesis); } /** * Based off of RALEE code ralee-get-base-pairs. Keeps track of open bracket * positions in "stack" vector. When a close bracket is reached, pair this - * with the last element in the "stack" vector and store in "pairs" vector. - * Remove last element in the "stack" vector. Continue in this manner until - * the whole string is processed. + * with the last matching element in the "stack" vector and store in "pairs" + * vector. Remove last element in the "stack" vector. Continue in this manner + * until the whole string is processed. Parse errors are thrown as exceptions + * wrapping the error location - position of the first unmatched closing + * bracket, or string length if there is an unmatched opening bracket. * * @param line * Secondary structure line of an RNA Stockholm file - * @return Array of SequenceFeature; type = RNA helix, begin is open base - * pair, end is close base pair + * @return + * @throw {@link WUSSParseException} */ - public static Vector GetSimpleBPs(CharSequence line) + public static Vector getSimpleBPs(CharSequence line) throws WUSSParseException { Hashtable> stacks = new Hashtable>(); @@ -128,13 +154,13 @@ public class Rna else if (isClosingParenthesis(base)) { - char opening = matchingOpeningParenthesis(base); + char opening = getMatchingOpeningParenthesis(base); if (!stacks.containsKey(opening)) { throw new WUSSParseException(MessageManager.formatMessage( "exception.mismatched_unseen_closing_char", - new String[] { new StringBuffer(base).toString() }), i); + new String[] { String.valueOf(base) }), i); } Stack stack = stacks.get(opening); @@ -143,7 +169,7 @@ public class Rna // error whilst parsing i'th position. pass back throw new WUSSParseException(MessageManager.formatMessage( "exception.mismatched_closing_char", - new String[] { new StringBuffer(base).toString() }), i); + new String[] { String.valueOf(base) }), i); } int temp = stack.pop(); @@ -156,33 +182,36 @@ public class Rna Stack stack = stacks.get(opening); if (!stack.empty()) { + /* + * we have an unmatched opening bracket; report error as at + * i (length of input string) + */ throw new WUSSParseException(MessageManager.formatMessage( "exception.mismatched_opening_char", - new String[] { new StringBuffer(opening).toString(), - Integer.valueOf(stack.pop()).toString() }), i); + new String[] { String.valueOf(opening), + String.valueOf(stack.pop()) }), i); } } return pairs; } - public static SequenceFeature[] GetBasePairs(CharSequence line) + public static SequenceFeature[] getBasePairs(List bps) throws WUSSParseException { - Vector bps = GetSimpleBPs(line); SequenceFeature[] outPairs = new SequenceFeature[bps.size()]; for (int p = 0; p < bps.size(); p++) { - SimpleBP bp = bps.elementAt(p); + SimpleBP bp = bps.get(p); outPairs[p] = new SequenceFeature("RNA helix", "", "", bp.getBP5(), bp.getBP3(), ""); } return outPairs; } - public static ArrayList GetModeleBP(CharSequence line) + public static List getModeleBP(CharSequence line) throws WUSSParseException { - Vector bps = GetSimpleBPs(line); + Vector bps = getSimpleBPs(line); return new ArrayList(bps); } @@ -220,8 +249,8 @@ public class Rna int close; // Position of a close bracket under review int j; // Counter - Hashtable helices = new Hashtable(); // Keep track of helix number for each - // position + Hashtable helices = new Hashtable(); + // Keep track of helix number for each position // Go through each base pair and assign positions a helix for (i = 0; i < pairs.length; i++) @@ -255,7 +284,7 @@ public class Rna if ((popen < lastopen) && (popen > open)) { if (helices.containsValue(popen) - && (((Integer) helices.get(popen)) == helix)) + && ((helices.get(popen)) == helix)) { continue; } @@ -281,4 +310,138 @@ public class Rna } } + + /** + * Answers true if the character is a recognised symbol for RNA secondary + * structure. Currently accepts a-z, A-Z, ()[]{}<>. + * + * @param c + * @return + */ + public static boolean isRnaSecondaryStructureSymbol(char c) + { + return isOpeningParenthesis(c) || isClosingParenthesis(c); + } + + /** + * Answers true if the string is a recognised symbol for RNA secondary + * structure. Currently accepts a-z, A-Z, ()[]{}<>. + * + * @param s + * @return + */ + public static boolean isRnaSecondaryStructureSymbol(String s) + { + return isOpeningParenthesis(s) || isClosingParenthesis(s); + } + + /** + * Translates a string to RNA secondary structure representation. Returns the + * string with any non-SS characters changed to spaces. Accepted characters + * are a-z, A-Z, and (){}[]<> brackets. + * + * @param ssString + * @return + */ + public static String getRNASecStrucState(String ssString) + { + if (ssString == null) + { + return null; + } + StringBuilder result = new StringBuilder(ssString.length()); + for (int i = 0; i < ssString.length(); i++) + { + char c = ssString.charAt(i); + result.append(isRnaSecondaryStructureSymbol(c) ? c : " "); + } + return result.toString(); + } + + /** + * Answers true if the base-pair is either a canonical (A-T/U, C-G) or a + * wobble (G-T/U) pair (either way round), else false + * + * @param first + * @param second + * @return + */ + public static boolean isCanonicalOrWobblePair(char first, char second) + { + if (first > 'Z') + { + first -= 32; + } + if (second > 'Z') + { + second -= 32; + } + + switch (first) + { + case 'A': + switch (second) + { + case 'T': + case 'U': + return true; + } + break; + case 'C': + switch (second) + { + case 'G': + return true; + } + break; + case 'T': + case 'U': + switch (second) + { + case 'A': + case 'G': + return true; + } + break; + case 'G': + switch (second) + { + case 'C': + case 'T': + case 'U': + return true; + } + break; + } + return false; + } + + /** + * Returns the matching close pair symbol for the given opening symbol. + * Currently returns a-z for A-Z, or )]}> for ([{<, or the input symbol if it + * is not a valid opening symbol. + * + * @param c + * @return + */ + public static char getMatchingClosingParenthesis(char c) + { + if ('A' <= c && c <= 'Z') + { + return (char) (c + 'a' - 'A'); + } + switch (c) + { + case '(': + return ')'; + case '[': + return ']'; + case '{': + return '}'; + case '<': + return '>'; + default: + return c; + } + } } diff --git a/src/jalview/analysis/SequenceIdMatcher.java b/src/jalview/analysis/SequenceIdMatcher.java index 70defb0..c12de4e 100755 --- a/src/jalview/analysis/SequenceIdMatcher.java +++ b/src/jalview/analysis/SequenceIdMatcher.java @@ -290,7 +290,7 @@ public class SequenceIdMatcher { if (s != null) { - id = new String(s); + id = s.toLowerCase(); } else { @@ -314,13 +314,13 @@ public class SequenceIdMatcher } if (s instanceof SeqIdName) { - return this.equals(((SeqIdName) s).id); + return this.stringequals(((SeqIdName) s).id); } else { if (s instanceof String) { - return this.equals((String) s); + return this.stringequals(((String) s).toLowerCase()); } } @@ -344,7 +344,7 @@ public class SequenceIdMatcher * @param s * @return boolean */ - public boolean equals(String s) + private boolean stringequals(String s) { if (id.length() > s.length()) { @@ -357,5 +357,15 @@ public class SequenceIdMatcher .indexOf(s.charAt(id.length())) > -1)) : false; } } + + /** + * toString method returns the wrapped sequence id. For debugging purposes + * only, behaviour not guaranteed not to change. + */ + @Override + public String toString() + { + return id; + } } } diff --git a/src/jalview/analysis/StructureFrequency.java b/src/jalview/analysis/StructureFrequency.java index 88387dd..479c8562 100644 --- a/src/jalview/analysis/StructureFrequency.java +++ b/src/jalview/analysis/StructureFrequency.java @@ -24,6 +24,7 @@ import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.util.Comparison; import jalview.util.Format; import java.util.ArrayList; @@ -103,23 +104,23 @@ public class StructureFrequency SequenceFeature[] rna = rnaStruc._rnasecstr; char c, s, cEnd; - int count = 0, nonGap = 0, i, bpEnd = -1, j, jSize = sequences.length; + int bpEnd = -1; + int jSize = sequences.length; int[] values; int[][] pairs; float percentage; - boolean wooble = true; - for (i = start; i < end; i++) // foreach column + + for (int i = start; i < end; i++) // foreach column { - residueHash = new Hashtable(); + int canonicalOrWobblePairCount = 0; + int otherPairCount = 0; maxResidue = "-"; values = new int[255]; pairs = new int[255][255]; bpEnd = -1; - // System.out.println("s="+struc[i]); if (i < struc.length) { s = struc[i]; - } else { @@ -130,7 +131,7 @@ public class StructureFrequency s = '-'; } - if (s != '(' && s != '[') + if (!Rna.isOpeningParenthesis(s)) { if (s == '-') { @@ -139,12 +140,11 @@ public class StructureFrequency } else { - bpEnd = findPair(rna, i); if (bpEnd > -1) { - for (j = 0; j < jSize; j++) // foreach row + for (int j = 0; j < jSize; j++) // foreach row { if (sequences[j] == null) { @@ -152,45 +152,46 @@ public class StructureFrequency .println("WARNING: Consensus skipping null sequence - possible race condition."); continue; } - c = sequences[j].getCharAt(i); - // System.out.println("c="+c); - // standard representation for gaps in sequence and structure - if (c == '.' || c == ' ') - { - c = '-'; - } + c = sequences[j].getCharAt(i); + cEnd = sequences[j].getCharAt(bpEnd); - if (c == '-') + if (Comparison.isGap(c) || Comparison.isGap(cEnd)) { values['-']++; continue; } - cEnd = sequences[j].getCharAt(bpEnd); - // System.out.println("pairs ="+c+","+cEnd); - if (checkBpType(c, cEnd) == true) + /* + * ensure upper-case for counting purposes + */ + if ('a' <= c && 'z' >= c) + { + c += 'A' - 'a'; + } + if ('a' <= cEnd && 'z' >= cEnd) + { + cEnd += 'A' - 'a'; + } + if (Rna.isCanonicalOrWobblePair(c, cEnd)) { - values['(']++; // H means it's a helix (structured) + values['(']++; maxResidue = "("; - wooble = true; - // System.out.println("It's a pair wc"); - + canonicalOrWobblePairCount++; } - if (checkBpType(c, cEnd) == false) + else { - wooble = false; - values['[']++; // H means it's a helix (structured) + values['[']++; maxResidue = "["; - + otherPairCount++; } pairs[c][cEnd]++; - } } // nonGap++; } - // UPDATE this for new values + + residueHash = new Hashtable(); if (profile) { // TODO 1-dim array with jsize in [0], nongapped in [1]; or Pojo @@ -199,13 +200,21 @@ public class StructureFrequency residueHash.put(PAIRPROFILE, pairs); } - if (wooble == true) - { - count = values['(']; - } - if (wooble == false) + + /* + * the count is the number of valid pairs (as a percentage, determines + * the relative size of the profile logo) + */ + int count = canonicalOrWobblePairCount; + + /* + * currently displaying as '(' if most pairs are valid, or as + * '[' if there are more invalid than valid pairs + */ + if (!maxResidue.equals("-")) { - count = values['[']; + maxResidue = canonicalOrWobblePairCount >= otherPairCount ? "(" + : "["; } residueHash.put(MAXCOUNT, new Integer(count)); residueHash.put(MAXRESIDUE, maxResidue); @@ -225,17 +234,9 @@ public class StructureFrequency values[']'] = values['[']; values['('] = 0; values['['] = 0; + maxResidue = maxResidue.equals("(") ? ")" : "]"; + residueHash = new Hashtable(); - if (wooble == true) - { - // System.out.println(maxResidue+","+wooble); - maxResidue = ")"; - } - if (wooble == false) - { - // System.out.println(maxResidue+","+wooble); - maxResidue = "]"; - } if (profile) { residueHash.put(PROFILE, new int[][] { values, @@ -251,80 +252,8 @@ public class StructureFrequency residueHash.put(PID_GAPS, new Float(percentage)); result[bpEnd] = residueHash; - - } - } - } - - /** - * Method to check if a base-pair is a canonical or a wobble bp - * - * @param up - * 5' base - * @param down - * 3' base - * @return True if it is a canonical/wobble bp - */ - public static boolean checkBpType(char up, char down) - { - if (up > 'Z') - { - up -= 32; - } - if (down > 'Z') - { - down -= 32; - } - - switch (up) - { - case 'A': - switch (down) - { - case 'T': - return true; - case 'U': - return true; - } - break; - case 'C': - switch (down) - { - case 'G': - return true; - } - break; - case 'T': - switch (down) - { - case 'A': - return true; - case 'G': - return true; - } - break; - case 'G': - switch (down) - { - case 'C': - return true; - case 'T': - return true; - case 'U': - return true; - } - break; - case 'U': - switch (down) - { - case 'A': - return true; - case 'G': - return true; } - break; } - return false; } /** @@ -534,7 +463,7 @@ public class StructureFrequency for (String j : test) { System.out.println(i + "-" + j + ": " - + StructureFrequency.checkBpType(i.charAt(0), j.charAt(0))); + + Rna.isCanonicalOrWobblePair(i.charAt(0), j.charAt(0))); } } } diff --git a/src/jalview/api/AlignCalcManagerI.java b/src/jalview/api/AlignCalcManagerI.java index 66f4036..18605b8 100644 --- a/src/jalview/api/AlignCalcManagerI.java +++ b/src/jalview/api/AlignCalcManagerI.java @@ -35,17 +35,12 @@ public interface AlignCalcManagerI void notifyStart(AlignCalcWorkerI worker); /** - * check if a calculation of this type is already active - * - * @param worker - * @return - */ - boolean alreadyDoing(AlignCalcWorkerI worker); - - /** - * tell manager that worker is now processing data + * tell manager that a thread running worker's run() loop is ready to start + * processing data * * @param worker + * @return true if worker should start processing, false if another thread is + * in progress */ boolean notifyWorking(AlignCalcWorkerI worker); @@ -63,7 +58,7 @@ public interface AlignCalcManagerI * * @param worker */ - void workerCannotRun(AlignCalcWorkerI worker); + void disableWorker(AlignCalcWorkerI worker); /** * indicate that a worker like this may be run on the platform. @@ -71,7 +66,15 @@ public interface AlignCalcManagerI * @param worker * of class to be removed from the execution blacklist */ - void workerMayRun(AlignCalcWorkerI worker); + void enableWorker(AlignCalcWorkerI worker); + + /** + * Answers true if the worker is disabled from running + * + * @param worker + * @return + */ + boolean isDisabled(AlignCalcWorkerI worker); /** * launch a new worker @@ -83,7 +86,7 @@ public interface AlignCalcManagerI /** * * @param worker - * @return + * @return true if the worker is currently running */ boolean isWorking(AlignCalcWorkerI worker); @@ -120,7 +123,7 @@ public interface AlignCalcManagerI * * @param workerClass */ - void updateAnnotationFor(Class workerClass); + void updateAnnotationFor(Class workerClass); /** * return any registered workers of the given class @@ -128,17 +131,8 @@ public interface AlignCalcManagerI * @param workerClass * @return null or one or more workers of the given class */ - List getRegisteredWorkersOfClass(Class workerClass); - - /** - * start any workers of the given class - * - * @param workerClass - * @return false if no workers of given class were registered (note - - * blacklisted classes cannot be restarted, so this method will return - * true for blacklisted workers) - */ - boolean startRegisteredWorkersOfClass(Class workerClass); + List getRegisteredWorkersOfClass( + Class workerClass); /** * work out if there is an instance of a worker that is *waiting* to start @@ -156,6 +150,15 @@ public interface AlignCalcManagerI * * @param typeToRemove */ - void removeRegisteredWorkersOfClass(Class typeToRemove); + void removeRegisteredWorkersOfClass( + Class typeToRemove); + /** + * Removes the worker that produces the given annotation, provided it is + * marked as 'deletable'. Some workers may need to continue to run as the + * results of their calculations are needed, e.g. for colour schemes. + * + * @param ann + */ + void removeWorkerForAnnotation(AlignmentAnnotation ann); } diff --git a/src/jalview/api/AlignCalcWorkerI.java b/src/jalview/api/AlignCalcWorkerI.java index 06dc054..85157c4 100644 --- a/src/jalview/api/AlignCalcWorkerI.java +++ b/src/jalview/api/AlignCalcWorkerI.java @@ -35,17 +35,26 @@ public interface AlignCalcWorkerI extends Runnable * @param annot * @return */ - public boolean involves(AlignmentAnnotation annot); + 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. + * the values). This allows ßquick redraw of annotations when display settings + * are changed. */ - public void updateAnnotation(); + void updateAnnotation(); /** - * Removes any annotation managed by this worker from the alignment + * Removes any annotation(s) managed by this worker from the alignment */ void removeAnnotation(); + + /** + * Answers true if the worker should be deleted entirely when its annotation + * is deleted from the display, or false if it should continue to run. Some + * workers are required to run for their side-effects. + * + * @return + */ + boolean isDeletable(); } diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index c7d7a37..f92160e 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -52,6 +52,18 @@ public interface AlignViewportI extends ViewStyleI */ public int calcPanelHeight(); + /** + * Answers true if the viewport has at least one column selected + * + * @return + */ + boolean hasSelectedColumns(); + + /** + * Answers true if the viewport has at least one hidden column + * + * @return + */ boolean hasHiddenColumns(); boolean isValidCharWidth(); @@ -234,11 +246,30 @@ public interface AlignViewportI extends ViewStyleI * This method returns the visible alignment as text, as seen on the GUI, ie * if columns are hidden they will not be returned in the result. Use this for * calculating trees, PCA, redundancy etc on views which contain hidden + * columns. This method doesn't exclude hidden sequences from the output. + * + * @param selectedRegionOnly + * - determines if only the selected region or entire alignment is + * exported + * @return String[] + */ + String[] getViewAsString(boolean selectedRegionOnly); + + /** + * This method returns the visible alignment as text, as seen on the GUI, ie + * if columns are hidden they will not be returned in the result. Use this for + * calculating trees, PCA, redundancy etc on views which contain hidden * columns. * + * @param selectedRegionOnly + * - determines if only the selected region or entire alignment is + * exported + * @param isExportHiddenSeqs + * - determines if hidden sequences would be exported or not. + * * @return String[] */ - String[] getViewAsString(boolean selectedRegionOnly); + String[] getViewAsString(boolean selectedRegionOnly, boolean isExportHiddenSeqs); void setSelectionGroup(SequenceGroup sg); @@ -375,5 +406,14 @@ public interface AlignViewportI extends ViewStyleI */ void setFollowHighlight(boolean b); + public void applyFeaturesStyle(FeatureSettingsModelI featureSettings); + + /** + * check if current selection group is defined on the view, or is simply a + * temporary group. + * + * @return true if group is defined on the alignment + */ + boolean isSelectionDefinedGroup(); } diff --git a/src/jalview/api/DBRefEntryI.java b/src/jalview/api/DBRefEntryI.java index 9415745..52ee381 100644 --- a/src/jalview/api/DBRefEntryI.java +++ b/src/jalview/api/DBRefEntryI.java @@ -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 { @@ -47,30 +48,47 @@ public interface DBRefEntryI public void setVersion(String version); /** + * access a mapping, if present that can be used to map positions from the + * associated dataset sequence to the DBRef's sequence frame. * - * @param startRes - * index of start residue in the source DB + * @return null or a valid mapping. */ - public void setStartRes(int startRes); - - /** - * - * @return index of start residue in the source DB - */ - public int getStartRes(); + public Mapping getMap(); /** + * Answers true if this object is either equivalent to, or can be 'improved' + * by, the given entry. Specifically, answers true if + *
          + *
        • source and accession are identical
        • + *
        • version is identical, or this version is of the format "someSource:0", + * in which case the version for the other entry replaces it
        • + *
        • mappings are not compared but if this entry has no mapping, replace + * with that for the other entry
        • + *
        * - * @param endRes - * index of end residue in the source DB + * @param otherEntry + * @return */ - public void setEndRes(int endRes); + public boolean updateFrom(DBRefEntryI otherEntry); /** - * - * @return index of end residue in the source DB + * Answers true if the ref looks like a primary (direct) database reference.
        + * The only way a dbref's mappings can be fully verified is via the local + * sequence frame, so rather than use isPrimaryCandidate directly, please use + * SequenceI.getPrimaryDbRefs().
        + * Primary references indicate the local sequence data directly corresponds + * with the database record. All other references are secondary. Direct + * references indicate that part or all of the local sequence data can be + * mapped with another sequence, enabling annotation transfer. + * Cross-references indicate the local sequence data can be corresponded to + * some other linear coordinate system via a transformation.
        + * This method is also sufficient to distinguish direct DBRefEntry mappings + * from other relationships - e.g. coding relationships (imply a 1:3/3:1 + * mapping), but not transcript relationships, which imply a (possibly + * non-contiguous) 1:1 mapping. + * + * @return true if this reference provides a primary accession for the + * associated sequence object */ - public int getEndRes(); - - public Mapping getMap(); + public boolean isPrimaryCandidate(); } diff --git a/src/jalview/api/FeatureColourI.java b/src/jalview/api/FeatureColourI.java index f68d7a7..922a038 100644 --- a/src/jalview/api/FeatureColourI.java +++ b/src/jalview/api/FeatureColourI.java @@ -6,8 +6,7 @@ public interface FeatureColourI { /** - * Answers true when either isColourByLabel, isAboveThreshold or - * isBelowThreshold answers true + * Answers true when the feature colour varies across the score range * * @return */ @@ -35,13 +34,22 @@ public interface FeatureColourI ColorI getMaxColour(); /** - * Answers true if the feature is coloured by label (description); only - * applicable when isGraduatedColour answers true + * Answers true if the feature has a single colour, i.e. if isColourByLabel() + * and isGraduatedColour() both answer false + * + * @return + */ + boolean isSimpleColour(); + + /** + * Answers true if the feature is coloured by label (description) * * @return */ boolean isColourByLabel(); + void setColourByLabel(boolean b); + /** * Answers true if the feature is coloured below a threshold value; only * applicable when isGraduatedColour answers true @@ -50,6 +58,8 @@ public interface FeatureColourI */ boolean isBelowThreshold(); + void setBelowThreshold(boolean b); + /** * Answers true if the feature is coloured above a threshold value; only * applicable when isGraduatedColour answers true @@ -58,14 +68,20 @@ public interface FeatureColourI */ boolean isAboveThreshold(); + void setAboveThreshold(boolean b); + /** - * Answers true if the threshold is the min (or max) of the colour range; only - * applicable when isGraduatedColour answers true + * Answers true if the threshold is the minimum value (when + * isAboveThreshold()) or maximum value (when isBelowThreshold()) of the + * colour range; only applicable when isGraduatedColour and either + * isAboveThreshold() or isBelowThreshold() answers true * * @return */ boolean isThresholdMinMax(); + void setThresholdMinMax(boolean b); + /** * Returns the threshold value (if any), else zero * @@ -73,14 +89,7 @@ public interface FeatureColourI */ float getThreshold(); - float getMax(); - - /** - * Returns the minimum score of the graduated colour range - * - * @return - */ - float getMin(); + void setThreshold(float f); /** * Answers true if either isAboveThreshold or isBelowThreshold answers true @@ -90,12 +99,15 @@ public interface FeatureColourI boolean hasThreshold(); /** - * Returns the computed colour for the given sequence feature + * Answers true if the colour varies between the actual minimum and maximum + * score values of the feature, or false if between absolute minimum and + * maximum values (or if not a graduated colour). * - * @param feature * @return */ - ColorI getColor(SequenceFeature feature); + boolean isAutoScaled(); + + void setAutoScaled(boolean b); /** * Answers true if the feature has a simple colour, or is coloured by label, @@ -123,21 +135,25 @@ public interface FeatureColourI */ String toJalviewFormat(String featureType); - void setThreshold(float f); - - boolean isAutoScaled(); - - void setAutoScaled(boolean b); - - boolean isSimpleColour(); - - void setAboveThreshold(boolean b); - - void setThresholdMinMax(boolean b); - - void setBelowThreshold(boolean b); + /** + * Returns the maximum score of the graduated colour range + * + * @return + */ + float getMax(); - void setColourByLabel(boolean b); + /** + * Returns the minimum score of the graduated colour range + * + * @return + */ + float getMin(); - void setGraduatedColour(boolean b); + /** + * Returns the computed colour for the given sequence feature + * + * @param feature + * @return + */ + ColorI getColor(SequenceFeature feature); } diff --git a/src/jalview/api/SiftsClientI.java b/src/jalview/api/SiftsClientI.java index e2fac14..dad434a 100644 --- a/src/jalview/api/SiftsClientI.java +++ b/src/jalview/api/SiftsClientI.java @@ -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 { /** @@ -45,12 +47,6 @@ public interface SiftsClientI */ public String getDbCoordSys(); - /** - * Get DB Evidence for the SIFTs Entry - * - * @return - */ - public String getDbEvidence(); /** * Get DB Source for the SIFTs Entry @@ -99,13 +95,6 @@ public interface SiftsClientI public boolean isAccessionMatched(String accessionId); /** - * Get the standard DB referenced by the SIFTs Entry - * - * @return - */ - public String[] getEntryDBs(); - - /** * * @param mop * MappingOutputPojo diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index 748954a..ec7cd25 100644 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -70,8 +70,6 @@ import java.util.Vector; public class APopupMenu extends java.awt.PopupMenu implements ActionListener, ItemListener { - private static final String ALL_ANNOTATIONS = "All"; - Menu groupMenu = new Menu(); MenuItem editGroupName = new MenuItem(); @@ -537,6 +535,7 @@ public class APopupMenu extends java.awt.PopupMenu implements MenuItem item = new MenuItem(label); item.addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(ActionEvent e) { ap.alignFrame.showURL(url, target); @@ -545,6 +544,7 @@ public class APopupMenu extends java.awt.PopupMenu implements linkMenu.add(item); } + @Override public void itemStateChanged(ItemEvent evt) { if (evt.getSource() == abovePIDColour) @@ -569,6 +569,7 @@ public class APopupMenu extends java.awt.PopupMenu implements } } + @Override public void actionPerformed(ActionEvent evt) { Object source = evt.getSource(); @@ -963,7 +964,7 @@ public class APopupMenu extends java.awt.PopupMenu implements "label.represent_group_with", new Object[] { "" })); revealAll.setLabel(MessageManager.getString("action.reveal_all")); revealSeq.setLabel(MessageManager.getString("action.reveal_sequences")); - menu1.setLabel(MessageManager.getString("label.group") + ":"); + menu1.setLabel(MessageManager.getString("label.group:")); add(groupMenu); this.add(seqMenu); this.add(hideSeqs); @@ -1299,35 +1300,7 @@ public class APopupMenu extends java.awt.PopupMenu implements void hideSequences(boolean representGroup) { - SequenceGroup sg = ap.av.getSelectionGroup(); - if (sg == null || sg.getSize() < 1) - { - ap.av.hideSequence(new SequenceI[] { seq }); - return; - } - - ap.av.setSelectionGroup(null); - - if (representGroup) - { - ap.av.hideRepSequences(seq, sg); - - return; - } - - int gsize = sg.getSize(); - SequenceI[] hseqs; - - hseqs = new SequenceI[gsize]; - - int index = 0; - for (int i = 0; i < gsize; i++) - { - hseqs[index++] = sg.getSequenceAt(i); - } - - ap.av.hideSequence(hseqs); - ap.av.sendSelection(); + ap.av.hideSequences(seq, representGroup); } /** @@ -1351,7 +1324,8 @@ public class APopupMenu extends java.awt.PopupMenu implements showMenu.removeAll(); hideMenu.removeAll(); - final List all = Arrays.asList(ALL_ANNOTATIONS); + final List all = Arrays.asList(new String[] { MessageManager + .getString("label.all") }); addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true); addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true, false); diff --git a/src/jalview/appletgui/AlignFrame.java b/src/jalview/appletgui/AlignFrame.java index 8f1f2fd..e36944f 100644 --- a/src/jalview/appletgui/AlignFrame.java +++ b/src/jalview/appletgui/AlignFrame.java @@ -219,6 +219,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, { viewport.setColumnSelection(columnSelection); } + viewport.setScaleAboveWrapped(scaleAbove.getState()); alignPanel = new AlignmentPanel(this, viewport); avc = new jalview.controller.AlignViewController(this, viewport, @@ -702,9 +703,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, // Hide everything by the current selection - this is a hack - we do the // invert and then hide // first check that there will be visible columns after the invert. - if ((viewport.getColumnSelection() != null - && viewport.getColumnSelection().getSelected() != null && viewport - .getColumnSelection().getSelected().size() > 0) + if (viewport.hasSelectedColumns() || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg .getEndRes())) { @@ -732,8 +731,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, hide = true; viewport.hideAllSelectedSeqs(); } - else if (!(toggleCols && viewport.getColumnSelection().getSelected() - .size() > 0)) + else if (!(toggleCols && viewport.hasSelectedColumns())) { viewport.showAllHiddenSeqs(); } @@ -741,7 +739,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, if (toggleCols) { - if (viewport.getColumnSelection().getSelected().size() > 0) + if (viewport.hasSelectedColumns()) { viewport.hideSelectedColumns(); if (!toggleSeqs) @@ -2233,7 +2231,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, } sg.setEndRes(viewport.getAlignment().getWidth() - 1); viewport.setSelectionGroup(sg); - alignPanel.paintAlignment(true); + // JAL-2034 - should delegate to + // alignPanel to decide if overview needs + // updating. + alignPanel.paintAlignment(false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2250,7 +2251,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, viewport.setSelectionGroup(null); alignPanel.idPanel.idCanvas.searchResults = null; alignPanel.seqPanel.seqCanvas.highlightSearchResults(null); - alignPanel.paintAlignment(true); + // JAL-2034 - should delegate to + // alignPanel to decide if overview needs + // updating. + alignPanel.paintAlignment(false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -3510,10 +3514,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener, nucleotideColour.setLabel(MessageManager.getString("label.nucleotide")); nucleotideColour.addActionListener(this); modifyPID.setLabel(MessageManager - .getString("label.modify_identity_thereshold")); + .getString("label.modify_identity_threshold")); modifyPID.addActionListener(this); modifyConservation.setLabel(MessageManager - .getString("label.modify_conservation_thereshold")); + .getString("label.modify_conservation_threshold")); modifyConservation.addActionListener(this); annotationColour.setLabel(MessageManager .getString("action.by_annotation")); diff --git a/src/jalview/appletgui/AnnotationColourChooser.java b/src/jalview/appletgui/AnnotationColourChooser.java index 15346a3..4b51d78 100644 --- a/src/jalview/appletgui/AnnotationColourChooser.java +++ b/src/jalview/appletgui/AnnotationColourChooser.java @@ -138,11 +138,11 @@ public class AnnotationColourChooser extends Panel implements } threshold.addItem(MessageManager - .getString("label.threshold_feature_no_thereshold")); + .getString("label.threshold_feature_no_threshold")); threshold.addItem(MessageManager - .getString("label.threshold_feature_above_thereshold")); + .getString("label.threshold_feature_above_threshold")); threshold.addItem(MessageManager - .getString("label.threshold_feature_below_thereshold")); + .getString("label.threshold_feature_below_threshold")); if (oldcs instanceof AnnotationColourGradient) { @@ -162,7 +162,7 @@ public class AnnotationColourChooser extends Panel implements default: throw new Error( MessageManager - .getString("error.implementation_error_dont_know_thereshold_annotationcolourgradient")); + .getString("error.implementation_error_dont_know_threshold_annotationcolourgradient")); } thresholdIsMin.setState(acg.thresholdIsMinMax); thresholdValue.setText("" + acg.getAnnotationThreshold()); diff --git a/src/jalview/appletgui/AnnotationLabels.java b/src/jalview/appletgui/AnnotationLabels.java index 5727e9c..b28ccc7 100755 --- a/src/jalview/appletgui/AnnotationLabels.java +++ b/src/jalview/appletgui/AnnotationLabels.java @@ -162,6 +162,7 @@ public class AnnotationLabels extends Panel implements ActionListener, return row; } + @Override public void actionPerformed(ActionEvent evt) { AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation(); @@ -261,6 +262,7 @@ public class AnnotationLabels extends Panel implements ActionListener, boolean resizePanel = false; + @Override public void mouseMoved(MouseEvent evt) { resizePanel = evt.getY() < 10 && evt.getX() < 14; @@ -306,6 +308,7 @@ public class AnnotationLabels extends Panel implements ActionListener, dragCancelled = true; } + @Override public void mouseDragged(MouseEvent evt) { if (dragCancelled) @@ -365,10 +368,12 @@ public class AnnotationLabels extends Panel implements ActionListener, } } + @Override public void mouseClicked(MouseEvent evt) { } + @Override public void mouseReleased(MouseEvent evt) { if (!resizePanel && !dragCancelled) @@ -400,6 +405,7 @@ public class AnnotationLabels extends Panel implements ActionListener, ap.annotationPanel.repaint(); } + @Override public void mouseEntered(MouseEvent evt) { if (evt.getY() < 10 && evt.getX() < 14) @@ -409,6 +415,7 @@ public class AnnotationLabels extends Panel implements ActionListener, } } + @Override public void mouseExited(MouseEvent evt) { dragCancelled = false; @@ -427,6 +434,7 @@ public class AnnotationLabels extends Panel implements ActionListener, repaint(); } + @Override public void mousePressed(MouseEvent evt) { oldY = evt.getY(); @@ -522,6 +530,7 @@ public class AnnotationLabels extends Panel implements ActionListener, final AlignmentAnnotation aaa = aa[selectedRow]; cbmi.addItemListener(new ItemListener() { + @Override public void itemStateChanged(ItemEvent e) { if (aaa.groupRef != null) @@ -545,6 +554,7 @@ public class AnnotationLabels extends Panel implements ActionListener, aa[selectedRow].groupRef.isShowConsensusHistogram()); chist.addItemListener(new ItemListener() { + @Override public void itemStateChanged(ItemEvent e) { // TODO: pass on reference @@ -564,6 +574,7 @@ public class AnnotationLabels extends Panel implements ActionListener, aa[selectedRow].groupRef.isShowSequenceLogo()); cprofl.addItemListener(new ItemListener() { + @Override public void itemStateChanged(ItemEvent e) { // TODO: pass on reference @@ -585,6 +596,7 @@ public class AnnotationLabels extends Panel implements ActionListener, aa[selectedRow].groupRef.isNormaliseSequenceLogo()); cprofn.addItemListener(new ItemListener() { + @Override public void itemStateChanged(ItemEvent e) { // TODO: pass on reference @@ -608,6 +620,7 @@ public class AnnotationLabels extends Panel implements ActionListener, av.isShowConsensusHistogram()); chist.addItemListener(new ItemListener() { + @Override public void itemStateChanged(ItemEvent e) { // TODO: pass on reference @@ -631,6 +644,7 @@ public class AnnotationLabels extends Panel implements ActionListener, av.isShowSequenceLogo()); cprof.addItemListener(new ItemListener() { + @Override public void itemStateChanged(ItemEvent e) { // TODO: pass on reference @@ -655,6 +669,7 @@ public class AnnotationLabels extends Panel implements ActionListener, av.isNormaliseSequenceLogo()); cprofn.addItemListener(new ItemListener() { + @Override public void itemStateChanged(ItemEvent e) { // TODO: pass on reference @@ -697,11 +712,47 @@ public class AnnotationLabels extends Panel implements ActionListener, // todo: make the ap scroll to the selection - not necessary, first // click highlights/scrolls, second selects ap.seqPanel.ap.idPanel.highlightSearchResults(null); - ap.av.setSelectionGroup(// new SequenceGroup( - aa[selectedRow].groupRef); // ); - ap.av.sendSelection(); + // process modifiers + SequenceGroup sg = ap.av.getSelectionGroup(); + if (sg == null + || sg == aa[selectedRow].groupRef + || !(jalview.util.Platform.isControlDown(evt) || evt + .isShiftDown())) + { + if (jalview.util.Platform.isControlDown(evt) + || evt.isShiftDown()) + { + // clone a new selection group from the associated group + ap.av.setSelectionGroup(new SequenceGroup( + aa[selectedRow].groupRef)); + } + else + { + // set selection to the associated group so it can be edited + ap.av.setSelectionGroup(aa[selectedRow].groupRef); + } + } + else + { + // modify current selection with associated group + int remainToAdd = aa[selectedRow].groupRef.getSize(); + for (SequenceI sgs : aa[selectedRow].groupRef.getSequences()) + { + if (jalview.util.Platform.isControlDown(evt)) + { + sg.addOrRemove(sgs, --remainToAdd == 0); + } + else + { + // notionally, we should also add intermediate sequences from + // last added sequence ? + sg.addSequence(sgs, --remainToAdd == 0); + } + } + } ap.paintAlignment(false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); + ap.av.sendSelection(); } else { @@ -728,7 +779,8 @@ public class AnnotationLabels extends Panel implements ActionListener, // we make a copy rather than edit the current selection if no // modifiers pressed // see Enhancement JAL-1557 - if (!(evt.isControlDown() || evt.isShiftDown())) + if (!(jalview.util.Platform.isControlDown(evt) || evt + .isShiftDown())) { sg = new SequenceGroup(sg); sg.clear(); @@ -736,7 +788,7 @@ public class AnnotationLabels extends Panel implements ActionListener, } else { - if (evt.isControlDown()) + if (jalview.util.Platform.isControlDown(evt)) { sg.addOrRemove(aa[selectedRow].sequenceRef, true); } @@ -794,11 +846,13 @@ public class AnnotationLabels extends Panel implements ActionListener, } } + @Override public void update(Graphics g) { paint(g); } + @Override public void paint(Graphics g) { int w = getSize().width; diff --git a/src/jalview/appletgui/AnnotationPanel.java b/src/jalview/appletgui/AnnotationPanel.java index 77700d0..6012c1a 100755 --- a/src/jalview/appletgui/AnnotationPanel.java +++ b/src/jalview/appletgui/AnnotationPanel.java @@ -22,9 +22,13 @@ package jalview.appletgui; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; +import jalview.datamodel.SequenceI; import jalview.renderer.AnnotationRenderer; import jalview.renderer.AwtRenderPanelI; +import jalview.schemes.ResidueProperties; +import jalview.util.Comparison; import jalview.util.MessageManager; +import jalview.util.Platform; import java.awt.Color; import java.awt.Dimension; @@ -98,7 +102,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI, public AnnotationPanel(AlignmentPanel ap) { - MAC = new jalview.util.Platform().isAMac(); + new jalview.util.Platform(); + MAC = Platform.isAMac(); this.ap = ap; av = ap.av; setLayout(null); @@ -158,12 +163,12 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI, if (evt.getActionCommand().equals(REMOVE)) { - for (int sel : av.getColumnSelection().getSelected()) + for (int index : av.getColumnSelection().getSelected()) { - // TODO: JAL-2001 check if applet has faulty 'REMOVE' selected columns - // of - // annotation if selection includes hidden columns - anot[sel] = null; + if (av.getColumnSelection().isVisible(index)) + { + anot[index] = null; + } } } else if (evt.getActionCommand().equals(LABEL)) @@ -239,7 +244,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI, else if (evt.getActionCommand().equals(STEM)) { type = 'S'; - symbol = "\u03C3"; + int column = av.getColumnSelection().getSelectedRanges().get(0)[0]; + symbol = aa[activeRow].getDefaultRnaHelixSymbol(column); } if (!aa[activeRow].hasIcons) @@ -344,7 +350,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI, if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK && activeRow != -1) { - if (av.getColumnSelection() == null) + if (av.getColumnSelection() == null + || av.getColumnSelection().isEmpty()) { return; } @@ -352,10 +359,8 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI, PopupMenu pop = new PopupMenu( MessageManager.getString("label.structure_type")); MenuItem item; - /* - * Just display the needed structure options - */ - if (av.getAlignment().isNucleotide() == true) + + if (av.getAlignment().isNucleotide()) { item = new MenuItem(STEM); item.addActionListener(this); @@ -457,21 +462,67 @@ public class AnnotationPanel extends Panel implements AwtRenderPanelI, } } - int res = evt.getX() / av.getCharWidth() + av.getStartRes(); + int column = evt.getX() / av.getCharWidth() + av.getStartRes(); if (av.hasHiddenColumns()) { - res = av.getColumnSelection().adjustForHiddenColumns(res); + column = av.getColumnSelection().adjustForHiddenColumns(column); } - if (row > -1 && res < aa[row].annotations.length - && aa[row].annotations[res] != null) + if (row > -1 && column < aa[row].annotations.length + && aa[row].annotations[column] != null) { - StringBuffer text = new StringBuffer("Sequence position " + (res + 1)); - if (aa[row].annotations[res].description != null) + StringBuilder text = new StringBuilder(); + text.append(MessageManager.getString("label.column")).append(" ") + .append(column + 1); + String description = aa[row].annotations[column].description; + if (description != null && description.length() > 0) + { + text.append(" ").append(description); + } + + /* + * if the annotation is sequence-specific, show the sequence number + * in the alignment, and (if not a gap) the residue and position + */ + SequenceI seqref = aa[row].sequenceRef; + if (seqref != null) { - text.append(" " + aa[row].annotations[res].description); + int seqIndex = av.getAlignment().findIndex(seqref); + if (seqIndex != -1) + { + text.append(", ") + .append(MessageManager.getString("label.sequence")) + .append(" ").append(seqIndex + 1); + char residue = seqref.getCharAt(column); + if (!Comparison.isGap(residue)) + { + text.append(" "); + String name; + if (av.getAlignment().isNucleotide()) + { + name = ResidueProperties.nucleotideName.get(String + .valueOf(residue)); + text.append(" Nucleotide: ").append( + name != null ? name : residue); + } + else + { + name = 'X' == residue ? "X" : ('*' == residue ? "STOP" + : ResidueProperties.aa2Triplet.get(String + .valueOf(residue))); + text.append(" Residue: ").append( + name != null ? name : residue); + } + int residuePos = seqref.findPosition(column); + text.append(" (").append(residuePos).append(")"); + // int residuePos = seqref.findPosition(column); + // text.append(residue).append(" (") + // .append(residuePos).append(")"); + } + } } + ap.alignFrame.statusBar.setText(text.toString()); } } diff --git a/src/jalview/appletgui/AnnotationRowFilter.java b/src/jalview/appletgui/AnnotationRowFilter.java index 4cb5ede..fc49de5 100644 --- a/src/jalview/appletgui/AnnotationRowFilter.java +++ b/src/jalview/appletgui/AnnotationRowFilter.java @@ -186,11 +186,11 @@ public abstract class AnnotationRowFilter extends Panel protected void populateThresholdComboBox(Choice threshold) { threshold.addItem(MessageManager - .getString("label.threshold_feature_no_thereshold")); + .getString("label.threshold_feature_no_threshold")); threshold.addItem(MessageManager - .getString("label.threshold_feature_above_thereshold")); + .getString("label.threshold_feature_above_threshold")); threshold.addItem(MessageManager - .getString("label.threshold_feature_below_thereshold")); + .getString("label.threshold_feature_below_threshold")); } public jalview.datamodel.AlignmentAnnotation getCurrentAnnotation() diff --git a/src/jalview/appletgui/FeatureColourChooser.java b/src/jalview/appletgui/FeatureColourChooser.java index 7638b3b..d0834e6 100644 --- a/src/jalview/appletgui/FeatureColourChooser.java +++ b/src/jalview/appletgui/FeatureColourChooser.java @@ -194,11 +194,11 @@ public class FeatureColourChooser extends Panel implements ActionListener, jPanel4.setBackground(Color.white); threshold.addItemListener(this); threshold.addItem(MessageManager - .getString("label.threshold_feature_no_thereshold")); + .getString("label.threshold_feature_no_threshold")); threshold.addItem(MessageManager - .getString("label.threshold_feature_above_thereshold")); + .getString("label.threshold_feature_above_threshold")); threshold.addItem(MessageManager - .getString("label.threshold_feature_below_thereshold")); + .getString("label.threshold_feature_below_threshold")); thresholdValue.addActionListener(this); slider.setBackground(Color.white); slider.setEnabled(false); diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java index b979674..cd9495e 100644 --- a/src/jalview/appletgui/FeatureRenderer.java +++ b/src/jalview/appletgui/FeatureRenderer.java @@ -60,17 +60,9 @@ import java.util.Hashtable; public class FeatureRenderer extends jalview.renderer.seqfeatures.FeatureRenderer { - /** - * Creates a new FeatureRenderer object. - * - * @param av - * DOCUMENT ME! - */ - public FeatureRenderer(AlignmentViewport av) - { - super(); - this.av = av; - } + // Holds web links for feature groups and feature types + // in the form label|link + Hashtable featureLinks = null; static String lastFeatureAdded; @@ -84,6 +76,16 @@ public class FeatureRenderer extends FeatureColourPanel colourPanel; + /** + * Creates a new FeatureRenderer object. + * + * @param av + */ + public FeatureRenderer(AlignmentViewport av) + { + super(av); + } + class FeatureColourPanel extends Panel { String label = ""; @@ -249,23 +251,24 @@ public class FeatureRenderer extends tmp = new Panel(); panel.add(tmp); - tmp.add(new Label("Name: ", Label.RIGHT)); + tmp.add(new Label(MessageManager.getString("label.name:"), Label.RIGHT)); tmp.add(name); tmp = new Panel(); panel.add(tmp); - tmp.add(new Label("Group: ", Label.RIGHT)); + tmp.add(new Label(MessageManager.getString("label.group:"), Label.RIGHT)); tmp.add(source); tmp = new Panel(); panel.add(tmp); - tmp.add(new Label("Colour: ", Label.RIGHT)); + tmp.add(new Label(MessageManager.getString("label.colour"), Label.RIGHT)); tmp.add(colourPanel); bigPanel.add(panel, BorderLayout.NORTH); panel = new Panel(); - panel.add(new Label("Description: ", Label.RIGHT)); + panel.add(new Label(MessageManager.getString("label.description:"), + Label.RIGHT)); panel.add(new ScrollPane().add(description)); if (!newFeatures) @@ -273,9 +276,11 @@ public class FeatureRenderer extends bigPanel.add(panel, BorderLayout.SOUTH); panel = new Panel(); - panel.add(new Label(" Start:", Label.RIGHT)); + panel.add(new Label(MessageManager.getString("label.start"), + Label.RIGHT)); panel.add(start); - panel.add(new Label(" End:", Label.RIGHT)); + panel.add(new Label(MessageManager.getString("label.end"), + Label.RIGHT)); panel.add(end); bigPanel.add(panel, BorderLayout.CENTER); } diff --git a/src/jalview/appletgui/FeatureSettings.java b/src/jalview/appletgui/FeatureSettings.java index e765f31..79812ae 100755 --- a/src/jalview/appletgui/FeatureSettings.java +++ b/src/jalview/appletgui/FeatureSettings.java @@ -765,28 +765,29 @@ public class FeatureSettings extends Panel implements ItemListener, public void paint(Graphics g) { Dimension d = getSize(); - if (col.isColourByLabel()) + if (col != null) { - g.setColor(Color.white); - g.fillRect(d.width / 2, 0, d.width / 2, d.height); - /* - * g.setColor(Color.black); Font f=g.getFont().deriveFont(9); - * g.setFont(f); - * - * // g.setFont(g.getFont().deriveFont( // - * AffineTransform.getScaleInstance( // - * width/g.getFontMetrics().stringWidth("Label"), // - * height/g.getFontMetrics().getHeight()))); g.drawString("Label", - * width/2, 0); - */ - - } - else if (col.isGraduatedColour()) - { - Color maxCol = ColorUtils.getColor(col.getMaxColour()); - g.setColor(maxCol); - g.fillRect(d.width / 2, 0, d.width / 2, d.height); - + if (col.isColourByLabel()) + { + g.setColor(Color.white); + g.fillRect(d.width / 2, 0, d.width / 2, d.height); + /* + * g.setColor(Color.black); Font f=g.getFont().deriveFont(9); + * g.setFont(f); + * + * // g.setFont(g.getFont().deriveFont( // + * AffineTransform.getScaleInstance( // + * width/g.getFontMetrics().stringWidth("Label"), // + * height/g.getFontMetrics().getHeight()))); g.drawString("Label", + * width/2, 0); + */ + } + else if (col.isGraduatedColour()) + { + Color maxCol = ColorUtils.getColor(col.getMaxColour()); + g.setColor(maxCol); + g.fillRect(d.width / 2, 0, d.width / 2, d.height); + } } if (hasLink) @@ -797,11 +798,6 @@ public class FeatureSettings extends Panel implements ItemListener, } } - @Override - public void mousePressed(MouseEvent e) - { - } - /** * Hide columns containing (or not containing) a given feature type * @@ -822,4 +818,11 @@ public class FeatureSettings extends Panel implements ItemListener, } } + @Override + public void mousePressed(MouseEvent e) + { + // TODO Auto-generated method stub + + } + } diff --git a/src/jalview/appletgui/IdPanel.java b/src/jalview/appletgui/IdPanel.java index 8f24f11..36c2199 100755 --- a/src/jalview/appletgui/IdPanel.java +++ b/src/jalview/appletgui/IdPanel.java @@ -100,6 +100,7 @@ public class IdPanel extends Panel implements MouseListener, Tooltip tooltip; + @Override public void mouseMoved(MouseEvent e) { int seq = alignPanel.seqPanel.findSeq(e); @@ -188,6 +189,7 @@ public class IdPanel extends Panel implements MouseListener, tooltiptext = null; } + @Override public void mouseDragged(MouseEvent e) { mouseDragging = true; @@ -207,6 +209,7 @@ public class IdPanel extends Panel implements MouseListener, alignPanel.paintAlignment(false); } + @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() < 2) @@ -270,6 +273,7 @@ public class IdPanel extends Panel implements MouseListener, } } + @Override public void mouseEntered(MouseEvent e) { if (scrollThread != null) @@ -278,6 +282,7 @@ public class IdPanel extends Panel implements MouseListener, } } + @Override public void mouseExited(MouseEvent e) { if (av.getWrapAlignment()) @@ -297,6 +302,7 @@ public class IdPanel extends Panel implements MouseListener, } } + @Override public void mousePressed(MouseEvent e) { if (e.getClickCount() > 1) @@ -345,7 +351,8 @@ public class IdPanel extends Panel implements MouseListener, } if ((av.getSelectionGroup() == null) - || ((!e.isControlDown() && !e.isShiftDown()) && av + || ((!jalview.util.Platform.isControlDown(e) && !e + .isShiftDown()) && av .getSelectionGroup() != null)) { av.setSelectionGroup(new SequenceGroup()); @@ -401,6 +408,7 @@ public class IdPanel extends Panel implements MouseListener, } + @Override public void mouseReleased(MouseEvent e) { if (scrollThread != null) @@ -455,6 +463,7 @@ public class IdPanel extends Panel implements MouseListener, running = false; } + @Override public void run() { running = true; diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java index edf8645..39d578a 100755 --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@ -266,7 +266,8 @@ public class OverviewPanel extends Panel implements Runnable, { miniMe = null; int alwidth = av.getAlignment().getWidth(); - int alheight = av.getAlignment().getHeight(); + int alheight = av.getAlignment().getHeight() + + av.getAlignment().getHiddenSequences().getSize(); if (av.isShowSequenceFeatures()) { @@ -305,6 +306,10 @@ public class OverviewPanel extends Panel implements Runnable, AlignmentI alignment = av.getAlignment(); for (row = 0; row <= sequencesHeight; row++) { + if (resizeAgain) + { + break; + } if ((int) (row * sampleRow) == lastrow) { sameRow++; @@ -387,6 +392,10 @@ public class OverviewPanel extends Panel implements Runnable, { for (col = 0; col < width; col++) { + if (resizeAgain) + { + break; + } lastcol = (int) (col * sampleCol); { mg.translate(col, sequencesHeight); diff --git a/src/jalview/appletgui/ScalePanel.java b/src/jalview/appletgui/ScalePanel.java index 71ecb13..d2c1693 100755 --- a/src/jalview/appletgui/ScalePanel.java +++ b/src/jalview/appletgui/ScalePanel.java @@ -22,6 +22,8 @@ package jalview.appletgui; import jalview.datamodel.ColumnSelection; import jalview.datamodel.SequenceGroup; +import jalview.renderer.ScaleRenderer; +import jalview.renderer.ScaleRenderer.ScaleMark; import jalview.util.MessageManager; import java.awt.Color; @@ -36,6 +38,7 @@ import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; +import java.util.List; public class ScalePanel extends Panel implements MouseMotionListener, MouseListener @@ -406,64 +409,69 @@ public class ScalePanel extends Panel implements MouseMotionListener, // Fill the selected columns ColumnSelection cs = av.getColumnSelection(); - gg.setColor(new Color(220, 0, 0)); - int avcharWidth = av.getCharWidth(), avcharHeight = av.getCharHeight(); - for (int sel : cs.getSelected()) + int avCharWidth = av.getCharWidth(); + int avcharHeight = av.getCharHeight(); + if (cs != null) { - // TODO: JAL-2001 - provide a fast method to list visible selected in a - // given range - if (av.hasHiddenColumns()) + gg.setColor(new Color(220, 0, 0)); + boolean hasHiddenColumns = cs.hasHiddenColumns(); + for (int sel : cs.getSelected()) { - sel = av.getColumnSelection().findColumnPosition(sel); - } + // TODO: JAL-2001 - provide a fast method to list visible selected in a + // given range + if (hasHiddenColumns) + { + if (cs.isVisible(sel)) + { + sel = cs.findColumnPosition(sel); + } + else + { + continue; + } + } - if ((sel >= startx) && (sel <= endx)) - { - gg.fillRect((sel - startx) * avcharWidth, 0, avcharWidth, - getSize().height); + if ((sel >= startx) && (sel <= endx)) + { + gg.fillRect((sel - startx) * avCharWidth, 0, avCharWidth, + getSize().height); + } } } // 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; + List marks = new ScaleRenderer().calculateMarks(av, startx, + endx); - for (int i = scalestartx; i < endx; i += 5) + FontMetrics fm = gg.getFontMetrics(av.getFont()); + int y = avcharHeight; + int yOf = fm.getDescent(); + y -= yOf; + for (ScaleMark mark : marks) { - if ((i % 10) == 0) + boolean major = mark.major; + int mpos = mark.column; // (i - startx - 1) + String mstring = mark.text; + if (mstring != null) { - string = String.valueOf(av.getColumnSelection() - .adjustForHiddenColumns(i)); - if ((i - startx - 1) * avcharWidth > maxX) + if (mpos * avCharWidth > maxX) { - gg.drawString(string, (i - startx - 1) * avcharWidth, y); - maxX = (i - startx + 1) * avcharWidth + fm.stringWidth(string); + gg.drawString(mstring, mpos * avCharWidth, y); + maxX = (mpos + 2) * avCharWidth + fm.stringWidth(mstring); } - - gg.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2), - y + 2, - ((i - startx - 1) * avcharWidth) + (avcharWidth / 2), y - + (fm.getDescent() * 2)); - + } + if (major) + { + gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + 2, + (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2)); } else { - gg.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2), y - + fm.getDescent(), ((i - startx - 1) * avcharWidth) - + (avcharWidth / 2), y + (fm.getDescent() * 2)); + gg.drawLine((mpos * avCharWidth) + (avCharWidth / 2), y + yOf, + (mpos * avCharWidth) + (avCharWidth / 2), y + (yOf * 2)); } } @@ -473,33 +481,25 @@ public class ScalePanel extends Panel implements MouseMotionListener, int res; if (av.getShowHiddenMarkers()) { - for (int i = 0; i < av.getColumnSelection().getHiddenColumns() - .size(); i++) + int widthx = 1 + endx - startx; + for (int i = 0; i < cs.getHiddenColumns().size(); i++) { - res = av.getColumnSelection().findHiddenRegionPosition(i) - - startx; + res = cs.findHiddenRegionPosition(i) - startx; if (res < 0 || res > widthx) { 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); } } - - if (reveal != null && reveal[0] > startx && reveal[0] < endx) - { - gg.drawString(MessageManager.getString("label.reveal_columns"), - reveal[0] * avcharWidth, 0); - } } - } } diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java index 0e5c0e7..b3f5ac8 100755 --- a/src/jalview/appletgui/SeqCanvas.java +++ b/src/jalview/appletgui/SeqCanvas.java @@ -24,6 +24,8 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.SearchResults; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; +import jalview.renderer.ScaleRenderer; +import jalview.renderer.ScaleRenderer.ScaleMark; import jalview.util.ColorUtils; import jalview.viewmodel.AlignmentViewport; @@ -91,26 +93,30 @@ public class SeqCanvas extends Panel private void drawNorthScale(Graphics g, int startx, int endx, int ypos) { - int scalestartx = startx - startx % 10 + 10; - + updateViewport(); g.setColor(Color.black); - - // NORTH SCALE - for (int i = scalestartx; i < endx; i += 10) + for (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx, + endx)) { - int value = i; - if (av.hasHiddenColumns()) + int mpos = mark.column; // (i - startx - 1) + if (mpos < 0) { - value = av.getColumnSelection().adjustForHiddenColumns(value); + continue; } + String mstring = mark.text; - g.drawString(String.valueOf(value), (i - startx - 1) * avcharWidth, - ypos - (avcharHeight / 2)); - - g.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2), - (ypos + 2) - (avcharHeight / 2), - ((i - startx - 1) * avcharWidth) + (avcharWidth / 2), - ypos - 2); + if (mark.major) + { + if (mstring != null) + { + g.drawString(mstring, mpos * avcharWidth, ypos + - (avcharHeight / 2)); + } + g.drawLine((mpos * avcharWidth) + (avcharWidth / 2), (ypos + 2) + - (avcharHeight / 2), (mpos * avcharWidth) + + (avcharWidth / 2), + ypos - 2); + } } } diff --git a/src/jalview/appletgui/SeqPanel.java b/src/jalview/appletgui/SeqPanel.java index 479b746..a437960 100644 --- a/src/jalview/appletgui/SeqPanel.java +++ b/src/jalview/appletgui/SeqPanel.java @@ -919,6 +919,14 @@ public class SeqPanel extends Panel implements MouseMotionListener, Tooltip tooltip; + /** + * set when the current UI interaction has resulted in a change that requires + * overview shading to be recalculated. this could be changed to something + * more expressive that indicates what actually has changed, so selective + * redraws can be applied + */ + private boolean needOverviewUpdate; // TODO: refactor to avcontroller + @Override public void mouseDragged(MouseEvent evt) { @@ -1521,9 +1529,11 @@ public class SeqPanel extends Panel implements MouseMotionListener, { return; } - - stretchGroup.recalcConservation(); // always do this - annotation has own - // state + // always do this - annotation has own state + // but defer colourscheme update until hidden sequences are passed in + boolean vischange = stretchGroup.recalcConservation(true); + // here we rely on stretchGroup == av.getSelection() + needOverviewUpdate |= vischange && av.isSelectionDefinedGroup(); if (stretchGroup.cs != null) { stretchGroup.cs.alignmentChanged(stretchGroup, @@ -1540,11 +1550,12 @@ public class SeqPanel extends Panel implements MouseMotionListener, stretchGroup.getName()); } } + PaintRefresher.Refresh(ap, av.getSequenceSetId()); + ap.paintAlignment(needOverviewUpdate); + needOverviewUpdate =false; changeEndRes = false; changeStartRes = false; stretchGroup = null; - PaintRefresher.Refresh(ap, av.getSequenceSetId()); - ap.paintAlignment(true); av.sendSelection(); } @@ -1596,6 +1607,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, if (res > (stretchGroup.getStartRes() - 1)) { stretchGroup.setEndRes(res); + needOverviewUpdate |= av.isSelectionDefinedGroup(); } } else if (changeStartRes) @@ -1603,6 +1615,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, if (res < (stretchGroup.getEndRes() + 1)) { stretchGroup.setStartRes(res); + needOverviewUpdate |= av.isSelectionDefinedGroup(); } } @@ -1636,6 +1649,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, if (stretchGroup.getSequences(null).contains(nextSeq)) { stretchGroup.deleteSequence(seq, false); + needOverviewUpdate |= av.isSelectionDefinedGroup(); } else { @@ -1645,6 +1659,7 @@ public class SeqPanel extends Panel implements MouseMotionListener, } stretchGroup.addSequence(nextSeq, false); + needOverviewUpdate |= av.isSelectionDefinedGroup(); } } @@ -1813,9 +1828,12 @@ public class SeqPanel extends Panel implements MouseMotionListener, // do we want to thread this ? (contention with seqsel and colsel locks, I // suspect) - // rules are: colsel is copied if there is a real intersection between - // sequence selection - boolean repaint = false, copycolsel = true; + /* + * only copy colsel if there is a real intersection between + * sequence selection and this panel's alignment + */ + boolean repaint = false; + boolean copycolsel = false; if (av.getSelectionGroup() == null || !av.isSelectionGroupChanged(true)) { SequenceGroup sgroup = null; @@ -1832,11 +1850,9 @@ public class SeqPanel extends Panel implements MouseMotionListener, } sgroup = seqsel.intersect(av.getAlignment(), (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null); - if ((sgroup == null || sgroup.getSize() == 0) - && (colsel == null || colsel.isEmpty())) + if ((sgroup != null && sgroup.getSize() > 0)) { - // don't copy columns if the region didn't intersect. - copycolsel = false; + copycolsel = true; } } if (sgroup != null && sgroup.getSize() > 0) @@ -1964,7 +1980,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(); diff --git a/src/jalview/appletgui/SequenceRenderer.java b/src/jalview/appletgui/SequenceRenderer.java index a64686a..276ad9e 100755 --- a/src/jalview/appletgui/SequenceRenderer.java +++ b/src/jalview/appletgui/SequenceRenderer.java @@ -34,6 +34,8 @@ import java.awt.Graphics; public class SequenceRenderer implements jalview.api.SequenceRenderer { + final static int CHAR_TO_UPPER = 'A' - 'a'; + AlignViewport av; FontMetrics fm; @@ -259,7 +261,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer } if (currentSequenceGroup.getShowNonconserved()) { - s = getDisplayChar(srep, i, s, '.'); + s = getDisplayChar(srep, i, s, '.', currentSequenceGroup); } } else @@ -283,7 +285,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer } if (av.getShowUnconserved()) { - s = getDisplayChar(srep, i, s, '.'); + s = getDisplayChar(srep, i, s, '.', null); } } @@ -315,20 +317,43 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer } - private char getDisplayChar(final boolean usesrep, int position, char s, - char c) + /** + * Returns 'conservedChar' to represent the given position if the sequence + * character at that position is equal to the consensus (ignoring case), else + * returns the sequence character + * + * @param usesrep + * @param position + * @param sequenceChar + * @param conservedChar + * @return + */ + private char getDisplayChar(final boolean usesrep, int position, + char sequenceChar, char conservedChar, SequenceGroup currentGroup) { // TODO - use currentSequenceGroup rather than alignment // currentSequenceGroup.getConsensus() - char conschar = (usesrep) ? av.getAlignment().getSeqrep() - .getCharAt(position) - : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter - .charAt(0); - if (!jalview.util.Comparison.isGap(conschar) && s == conschar) + char conschar = (usesrep) ? (currentGroup == null + || position < currentGroup.getStartRes() + || position > currentGroup.getEndRes() ? av.getAlignment() + .getSeqrep().getCharAt(position) + : (currentGroup.getSeqrep() != null ? currentGroup.getSeqrep() + .getCharAt(position) : av.getAlignment().getSeqrep() + .getCharAt(position))) + : (currentGroup != null && currentGroup.getConsensus() != null + && position >= currentGroup.getStartRes() + && position <= currentGroup.getEndRes() && currentGroup + .getConsensus().annotations.length > position) ? currentGroup + .getConsensus().annotations[position].displayCharacter + .charAt(0) + : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter + .charAt(0); + if (!jalview.util.Comparison.isGap(conschar) + && (sequenceChar == conschar || sequenceChar + CHAR_TO_UPPER == conschar)) { - s = c; + sequenceChar = conservedChar; } - return s; + return sequenceChar; } boolean inCurrentSequenceGroup(int res) diff --git a/src/jalview/appletgui/SliderPanel.java b/src/jalview/appletgui/SliderPanel.java index 2fc15d0..2c53c08 100644 --- a/src/jalview/appletgui/SliderPanel.java +++ b/src/jalview/appletgui/SliderPanel.java @@ -132,7 +132,7 @@ public class SliderPanel extends Panel implements ActionListener, pid.cs = cs; } PIDSlider.setTitle(MessageManager - .formatMessage("label.percentage_identity_thereshold", + .formatMessage("label.percentage_identity_threshold", new String[] { source })); if (ap.av.getAlignment().getGroups() != null) diff --git a/src/jalview/appletgui/TreeCanvas.java b/src/jalview/appletgui/TreeCanvas.java index 626960d..9072a62 100755 --- a/src/jalview/appletgui/TreeCanvas.java +++ b/src/jalview/appletgui/TreeCanvas.java @@ -124,13 +124,13 @@ public class TreeCanvas extends Panel implements MouseListener, tree.findHeight(tree.getTopNode()); // Now have to calculate longest name based on the leaves - Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector()); + Vector leaves = tree.findLeaves(tree.getTopNode()); boolean has_placeholders = false; longestName = ""; for (int i = 0; i < leaves.size(); i++) { - SequenceNode lf = (SequenceNode) leaves.elementAt(i); + SequenceNode lf = leaves.elementAt(i); if (lf.isPlaceholder()) { @@ -528,12 +528,11 @@ public class TreeCanvas extends Panel implements MouseListener, } else { - Vector leaves = new Vector(); - tree.findLeaves(highlightNode, leaves); + Vector leaves = tree.findLeaves(highlightNode); for (int i = 0; i < leaves.size(); i++) { - SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i)) + SequenceI seq = (SequenceI) leaves.elementAt(i) .element(); treeSelectionChanged(seq); } @@ -633,13 +632,13 @@ public class TreeCanvas extends Panel implements MouseListener, (int) (Math.random() * 255), (int) (Math.random() * 255)); setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter()); - Vector l = tree.findLeaves( - (SequenceNode) tree.getGroups().elementAt(i), new Vector()); + Vector l = tree.findLeaves((SequenceNode) tree + .getGroups().elementAt(i)); - Vector sequences = new Vector(); + Vector sequences = new Vector(); for (int j = 0; j < l.size(); j++) { - SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j)) + SequenceI s1 = (SequenceI) l.elementAt(j) .element(); if (!sequences.contains(s1)) { diff --git a/src/jalview/bin/ArgsParser.java b/src/jalview/bin/ArgsParser.java new file mode 100644 index 0000000..9c8d0df --- /dev/null +++ b/src/jalview/bin/ArgsParser.java @@ -0,0 +1,97 @@ +package jalview.bin; + +import java.net.URLDecoder; +import java.util.Vector; + +/** + * Notes: this argParser does not distinguish between parameter switches, + * parameter values and argument text. If an argument happens to be identical to + * a parameter, it will be taken as such (even though it didn't have a '-' + * prefixing it). + * + * @author Andrew Waterhouse and JBP documented. + * + */ +public class ArgsParser +{ + Vector vargs = null; + + public ArgsParser(String[] args) + { + vargs = new Vector(); + for (int i = 0; i < args.length; i++) + { + String arg = args[i].trim(); + if (arg.charAt(0) == '-') + { + arg = arg.substring(1); + } + vargs.addElement(arg); + } + } + + /** + * check for and remove first occurence of arg+parameter in arglist. + * + * @param arg + * @return return the argument following the given arg if arg was in list. + */ + public String getValue(String arg) + { + return getValue(arg, false); + } + + public String getValue(String arg, boolean utf8decode) + { + int index = vargs.indexOf(arg); + String dc = null, ret = null; + if (index != -1) + { + ret = vargs.elementAt(index + 1).toString(); + vargs.removeElementAt(index); + vargs.removeElementAt(index); + if (utf8decode && ret != null) + { + try + { + dc = URLDecoder.decode(ret, "UTF-8"); + ret = dc; + } catch (Exception e) + { + // TODO: log failure to decode + } + } + } + return ret; + } + + /** + * check for and remove first occurence of arg in arglist. + * + * @param arg + * @return true if arg was present in argslist. + */ + public boolean contains(String arg) + { + if (vargs.contains(arg)) + { + vargs.removeElement(arg); + return true; + } + else + { + return false; + } + } + + public String nextValue() + { + return vargs.remove(0); + } + + public int getSize() + { + return vargs.size(); + } + +} \ No newline at end of file diff --git a/src/jalview/bin/Cache.java b/src/jalview/bin/Cache.java index 7911cd5..31dbeac 100755 --- a/src/jalview/bin/Cache.java +++ b/src/jalview/bin/Cache.java @@ -20,8 +20,8 @@ */ package jalview.bin; -import jalview.datamodel.DBRefSource; -import jalview.ws.dbsources.Pdb; +import jalview.datamodel.PDBEntry; +import jalview.structure.StructureImportSettings; import jalview.ws.dbsources.das.api.DasSourceRegistryI; import jalview.ws.dbsources.das.datamodel.DasSourceRegistry; import jalview.ws.sifts.SiftsSettings; @@ -38,6 +38,7 @@ import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; import java.util.Enumeration; +import java.util.Locale; import java.util.Properties; import java.util.TreeSet; @@ -227,8 +228,24 @@ public class Cache private final static String DEFAULT_CACHE_THRESHOLD_IN_DAYS = "2"; private final static String DEFAULT_FAIL_SAFE_PID_THRESHOLD = "30"; + + /** + * Allowed values are PDB or mmCIF + */ + private final static String PDB_DOWNLOAD_FORMAT = PDBEntry.Type.MMCIF + .toString(); - private final static String DEFAULT_STRUCTURE_FORMAT = DBRefSource.PDB; + private final static String DEFAULT_PDB_FILE_PARSER = StructureImportSettings.StructureParser.JMOL_PARSER + .toString(); + + /* + * a date formatter using a fixed (rather than the user's) locale; + * this ensures that date properties can be written and re-read successfully + * even if the user changes their locale setting + */ + private static final DateFormat date_format = SimpleDateFormat + .getDateTimeInstance(SimpleDateFormat.MEDIUM, + SimpleDateFormat.MEDIUM, Locale.UK); /** * Initialises the Jalview Application Log @@ -426,8 +443,12 @@ public class Cache System.out .println("Jalview Version: " + codeVersion + codeInstallation); - Pdb.setCurrentDefaultFomart(jalview.bin.Cache.getDefault( - "DEFAULT_STRUCTURE_FORMAT", DEFAULT_STRUCTURE_FORMAT)); + StructureImportSettings.setDefaultStructureFileFormat(jalview.bin.Cache + .getDefault( +"PDB_DOWNLOAD_FORMAT", PDB_DOWNLOAD_FORMAT)); + // StructureImportSettings + // .setDefaultPDBFileParser(jalview.bin.Cache.getDefault( + // "DEFAULT_PDB_FILE_PARSER", DEFAULT_PDB_FILE_PARSER)); // jnlpVersion will be null if we're using InstallAnywhere // Dont do this check if running in headless mode if (jnlpVersion == null @@ -873,31 +894,32 @@ public class Cache { setProperty(property, jalview.util.Format.getHexString(colour)); } - - public static final DateFormat date_format = SimpleDateFormat - .getDateTimeInstance(); - + /** - * store a date in a jalview property + * Stores a formatted date in a jalview property, using a fixed locale. * - * @param string - * @param time + * @param propertyName + * @param date + * @return the formatted date string */ - public static void setDateProperty(String property, Date time) + public static String setDateProperty(String propertyName, Date date) { - setProperty(property, date_format.format(time)); + String formatted = date_format.format(date); + setProperty(propertyName, formatted); + return formatted; } /** - * read a date stored in a jalview property + * Reads a date stored in a Jalview property, parses it (using a fixed locale + * format) and returns as a Date, or null if parsing fails * - * @param property - * @return valid date as stored by setDateProperty, or null + * @param propertyName + * @return * */ - public static Date getDateProperty(String property) + public static Date getDateProperty(String propertyName) { - String val = getProperty(property); + String val = getProperty(propertyName); if (val != null) { try @@ -906,7 +928,7 @@ public class Cache } catch (Exception ex) { System.err.println("Invalid or corrupt date in property '" - + property + "' : value was '" + val + "'"); + + propertyName + "' : value was '" + val + "'"); } } return null; diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index d0b3232..3294c26 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -20,27 +20,36 @@ */ package jalview.bin; +import groovy.lang.Binding; +import groovy.util.GroovyScriptEngine; + import jalview.gui.AlignFrame; import jalview.gui.Desktop; +import jalview.gui.PromptUserConfig; +import jalview.io.AppletFormatAdapter; import jalview.io.BioJsHTMLOutput; +import jalview.io.FileLoader; +import jalview.io.FormatAdapter; import jalview.io.HtmlSvgOutput; +import jalview.io.IdentifyFile; +import jalview.io.NewickFile; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ColourSchemeProperty; +import jalview.schemes.UserColourScheme; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.ws.jws2.Jws2Discoverer; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; -import java.net.URLDecoder; import java.security.AllPermission; import java.security.CodeSource; import java.security.PermissionCollection; @@ -62,6 +71,15 @@ import javax.swing.UIManager; */ public class Jalview { + /* + * singleton instance of this class + */ + private static Jalview instance; + + private Desktop desktop; + + public static AlignFrame currentAlignFrame; + static { // grab all the rights we can the JVM @@ -83,6 +101,71 @@ public class Jalview } /** + * keep track of feature fetching tasks. + * + * @author JimP + * + */ + class FeatureFetcher + { + /* + * TODO: generalise to track all jalview events to orchestrate batch + * processing events. + */ + + private int queued = 0; + + private int running = 0; + + public FeatureFetcher() + { + + } + + public void addFetcher(final AlignFrame af, + final Vector dasSources) + { + final long id = System.currentTimeMillis(); + queued++; + final FeatureFetcher us = this; + new Thread(new Runnable() + { + + @Override + public void run() + { + synchronized (us) + { + queued--; + running++; + } + + af.setProgressBar(MessageManager + .getString("status.das_features_being_retrived"), id); + af.featureSettings_actionPerformed(null); + af.featureSettings.fetchDasFeatures(dasSources, true); + af.setProgressBar(null, id); + synchronized (us) + { + running--; + } + } + }).start(); + } + + public synchronized boolean allFinished() + { + return queued == 0 && running == 0; + } + + } + + public static Jalview getInstance() + { + return instance; + } + + /** * main class for Jalview application * * @param args @@ -90,6 +173,15 @@ public class Jalview */ public static void main(String[] args) { + instance = new Jalview(); + instance.doMain(args); + } + + /** + * @param args + */ + void doMain(String[] args) + { System.setSecurityManager(null); System.out.println("Java version: " + System.getProperty("java.version")); @@ -163,7 +255,7 @@ public class Jalview try { Cache.initLogger(); - } catch (java.lang.NoClassDefFoundError error) + } catch (NoClassDefFoundError error) { error.printStackTrace(); System.out @@ -172,7 +264,7 @@ public class Jalview System.exit(0); } - Desktop desktop = null; + desktop = null; try { @@ -236,7 +328,6 @@ public class Jalview Cache.log.debug("Starting questionnaire with default url: " + defurl); desktop.checkForQuestionnaire(defurl); - } } } @@ -244,17 +335,19 @@ public class Jalview { System.err.println("CMD [-noquestionnaire] executed successfully!"); } - desktop.checkForNews(); - } - if (!isHeadlessMode()) - { + if (!aparser.contains("nonews")) + { + desktop.checkForNews(); + } + BioJsHTMLOutput.updateBioJS(); } String file = null, protocol = null, format = null, data = null; - jalview.io.FileLoader fileLoader = new jalview.io.FileLoader(!headless); - Vector getFeatures = null; // vector of das source nicknames to fetch + FileLoader fileLoader = new FileLoader(!headless); + Vector getFeatures = null; // vector of das source nicknames to + // fetch // features from // loading is done. String groovyscript = null; // script to execute after all loading is @@ -268,8 +361,8 @@ public class Jalview System.out.println("No files to open!"); System.exit(1); } - String vamsasImport = aparser.getValue("vdoc"), vamsasSession = aparser - .getValue("vsess"); + String vamsasImport = aparser.getValue("vdoc"); + String vamsasSession = aparser.getValue("vsess"); if (vamsasImport != null || vamsasSession != null) { if (desktop == null || headless) @@ -284,13 +377,13 @@ public class Jalview { try { - String viprotocol = jalview.io.AppletFormatAdapter + String viprotocol = AppletFormatAdapter .checkProtocol(vamsasImport); if (viprotocol == jalview.io.FormatAdapter.FILE) { inSession = desktop.vamsasImport(new File(vamsasImport)); } - else if (viprotocol == jalview.io.FormatAdapter.URL) + else if (viprotocol == FormatAdapter.URL) { inSession = desktop.vamsasImport(new URL(vamsasImport)); } @@ -367,7 +460,7 @@ public class Jalview if (!file.startsWith("http://")) { - if (!(new java.io.File(file)).exists()) + if (!(new File(file)).exists()) { System.out.println("Can't find " + file); if (headless) @@ -377,9 +470,9 @@ public class Jalview } } - protocol = jalview.io.AppletFormatAdapter.checkProtocol(file); + protocol = AppletFormatAdapter.checkProtocol(file); - format = new jalview.io.IdentifyFile().identify(file, protocol); + format = new IdentifyFile().identify(file, protocol); AlignFrame af = fileLoader.LoadFileWaitTillLoaded(file, protocol, format); @@ -389,19 +482,18 @@ public class Jalview } else { - Desktop.setCurrentAlignFrame(af); + setCurrentAlignFrame(af); data = aparser.getValue("colour", true); if (data != null) { data.replaceAll("%20", " "); - jalview.schemes.ColourSchemeI cs = jalview.schemes.ColourSchemeProperty - .getColour(af.getViewport().getAlignment(), data); + ColourSchemeI cs = ColourSchemeProperty.getColour(af + .getViewport().getAlignment(), data); if (cs == null) { - jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme( - "white"); + UserColourScheme ucs = new UserColourScheme("white"); ucs.parseAppletParameter(data); cs = ucs; } @@ -418,7 +510,7 @@ public class Jalview if (data != null) { af.parseFeaturesFile(data, - jalview.io.AppletFormatAdapter.checkProtocol(data)); + AppletFormatAdapter.checkProtocol(data)); // System.out.println("Added " + data); System.out.println("CMD groups[-" + data + "] executed successfully!"); @@ -427,7 +519,7 @@ public class Jalview if (data != null) { af.parseFeaturesFile(data, - jalview.io.AppletFormatAdapter.checkProtocol(data)); + AppletFormatAdapter.checkProtocol(data)); // System.out.println("Added " + data); System.out.println("CMD [-features " + data + "] executed successfully!"); @@ -475,8 +567,8 @@ public class Jalview { System.out.println("CMD [-tree " + data + "] executed successfully!"); - fin = new jalview.io.NewickFile(data, - jalview.io.AppletFormatAdapter.checkProtocol(data)); + fin = new NewickFile(data, + AppletFormatAdapter.checkProtocol(data)); if (fin != null) { af.getViewport().setCurrentTree( @@ -517,7 +609,7 @@ public class Jalview // Execute the groovy script after we've done all the rendering stuff // and before any images or figures are generated. System.out.println("Executing script " + groovyscript); - executeGroovyScript(groovyscript, new Object[] { desktop, af }); + executeGroovyScript(groovyscript, af); System.out.println("CMD groovy[" + groovyscript + "] executed successfully!"); groovyscript = null; @@ -530,14 +622,14 @@ public class Jalview if (format.equalsIgnoreCase("png")) { - af.createPNG(new java.io.File(file)); - imageName = (new java.io.File(file)).getName(); + af.createPNG(new File(file)); + imageName = (new File(file)).getName(); System.out.println("Creating PNG image: " + file); continue; } else if (format.equalsIgnoreCase("svg")) { - File imageFile = new java.io.File(file); + File imageFile = new File(file); imageName = imageFile.getName(); af.createSVG(imageFile); System.out.println("Creating SVG image: " + file); @@ -545,21 +637,21 @@ public class Jalview } else if (format.equalsIgnoreCase("html")) { - File imageFile = new java.io.File(file); + File imageFile = new File(file); imageName = imageFile.getName(); - new HtmlSvgOutput(new java.io.File(file), af.alignPanel); + new HtmlSvgOutput(new File(file), af.alignPanel); System.out.println("Creating HTML image: " + file); continue; } else if (format.equalsIgnoreCase("imgMap")) { - af.createImageMap(new java.io.File(file), imageName); + af.createImageMap(new File(file), imageName); System.out.println("Creating image map: " + file); continue; } else if (format.equalsIgnoreCase("eps")) { - File outputFile = new java.io.File(file); + File outputFile = new File(file); System.out.println("Creating EPS file: " + outputFile.getAbsolutePath()); af.createEPS(outputFile); @@ -619,7 +711,7 @@ public class Jalview } else { - format = new jalview.io.IdentifyFile().identify(file, protocol); + format = new IdentifyFile().identify(file, protocol); } startUpAlframe = fileLoader.LoadFileWaitTillLoaded(file, protocol, @@ -641,11 +733,10 @@ public class Jalview // Once all other stuff is done, execute any groovy scripts (in order) if (groovyscript != null) { - if (jalview.bin.Cache.groovyJarsPresent()) + if (Cache.groovyJarsPresent()) { System.out.println("Executing script " + groovyscript); - executeGroovyScript(groovyscript, new Object[] { desktop, - startUpAlframe }); + executeGroovyScript(groovyscript, startUpAlframe); } else { @@ -691,6 +782,7 @@ public class Jalview + "-eps FILE\tCreate EPS file FILE from alignment.\n" + "-questionnaire URL\tQueries the given URL for information about any Jalview user questionnaires.\n" + "-noquestionnaire\tTurn off questionnaire check.\n" + + "-nonews\tTurn off check for Jalview news.\n" + "-nousagestats\tTurn off google analytics tracking for this session.\n" + "-sortbytree OR -nosortbytree\tEnable or disable sorting of the given alignment by the given tree\n" // + @@ -712,8 +804,8 @@ public class Jalview /** * start a User Config prompt asking if we can log usage statistics. */ - jalview.gui.PromptUserConfig prompter = new jalview.gui.PromptUserConfig( - desktop.desktop, + PromptUserConfig prompter = new PromptUserConfig( + Desktop.desktop, "USAGESTATS", "Jalview Usage Statistics", "Do you want to help make Jalview better by enabling " @@ -749,14 +841,8 @@ public class Jalview * the Jalview Desktop object passed in to the groovy binding as the * 'Jalview' object. */ - private static void executeGroovyScript(String groovyscript, - Object[] jalviewContext) + private void executeGroovyScript(String groovyscript, AlignFrame af) { - if (jalviewContext == null) - { - System.err - .println("Sorry. Groovy support is currently only available when running with the Jalview GUI enabled."); - } /** * for scripts contained in files */ @@ -773,8 +859,8 @@ public class Jalview tfile = File.createTempFile("jalview", "groovy"); PrintWriter outfile = new PrintWriter(new OutputStreamWriter( new FileOutputStream(tfile))); - BufferedReader br = new BufferedReader( - new java.io.InputStreamReader(System.in)); + BufferedReader br = new BufferedReader(new InputStreamReader( + System.in)); String line = null; while ((line = br.readLine()) != null) { @@ -838,75 +924,23 @@ public class Jalview } } } - boolean success = false; try { - /* - * The following code performs the GroovyScriptEngine invocation using - * reflection, and is equivalent to this fragment from the embedding - * groovy documentation on the groovy site: import - * groovy.lang.Binding; import groovy.util.GroovyScriptEngine; - * - * String[] roots = new String[] { "/my/groovy/script/path" }; - * GroovyScriptEngine gse = new GroovyScriptEngine(roots); Binding binding - * = new Binding(); binding.setVariable("input", "world"); - * gse.run("hello.groovy", binding); - */ - Class[] bspec; - Object[] binding; - int blen = ((jalviewContext[0] == null) ? 0 : 1) - + ((jalviewContext[1] == null) ? 0 : 1); - String cnames[] = new String[] { "Jalview", "currentAlFrame" }; - bspec = new Class[blen * 2]; - binding = new Object[blen * 2]; - blen = 0; - ClassLoader cl = null; Map vbinding = new HashMap(); - for (int jc = 0; jc < jalviewContext.length; jc++) + vbinding.put("Jalview", this); + if (af != null) { - if (jalviewContext[jc] != null) - { - if (cl == null) - { - cl = jalviewContext[jc].getClass().getClassLoader(); - } - bspec[blen * 2] = String.class; - bspec[blen * 2 + 1] = Object.class; - binding[blen * 2] = cnames[jc]; - binding[blen * 2 + 1] = jalviewContext[jc]; - vbinding.put(cnames[jc], jalviewContext[jc]); - blen++; - } + vbinding.put("currentAlFrame", af); } - Class gbindingc = cl.loadClass("groovy.lang.Binding"); - Constructor gbcons; - Object gbinding; - try + Binding gbinding = new Binding(vbinding); + GroovyScriptEngine gse = new GroovyScriptEngine(new URL[] { sfile }); + gse.run(sfile.toString(), gbinding); + if ("STDIN".equals(groovyscript)) { - gbcons = gbindingc.getConstructor(Map.class); - gbinding = gbcons.newInstance(vbinding); - } catch (NoSuchMethodException x) - { - // old style binding config - using series of string/object values to - // setVariable. - gbcons = gbindingc.getConstructor(); - gbinding = gbcons.newInstance(); - java.lang.reflect.Method setvar = gbindingc.getMethod( - "setVariable", bspec); - setvar.invoke(gbinding, binding); + // delete temp file that we made - + // only if it was successfully executed + tfile.delete(); } - - Class gsec = cl.loadClass("groovy.util.GroovyScriptEngine"); - Constructor gseccons = gsec - .getConstructor(new Class[] { URL[].class }); // String[].class - // }); - Object gse = gseccons - .newInstance(new Object[] { new URL[] { sfile } }); // .toString() - // } }); - java.lang.reflect.Method run = gsec.getMethod("run", new Class[] { - String.class, gbindingc }); - run.invoke(gse, new Object[] { sfile.toString(), gbinding }); - success = true; } catch (Exception e) { System.err.println("Exception Whilst trying to execute file " + sfile @@ -914,12 +948,6 @@ public class Jalview e.printStackTrace(System.err); } - if (success && groovyscript.equals("STDIN")) - { - // delete temp file that we made - but only if it was successfully - // executed - tfile.delete(); - } } /** @@ -927,16 +955,15 @@ public class Jalview * * @return vector of DAS source nicknames to retrieve from */ - private static Vector checkDasArguments(ArgsParser aparser) + private static Vector checkDasArguments(ArgsParser aparser) { - Vector source = null; + Vector source = null; String data; String locsources = Cache.getProperty(Cache.DAS_LOCAL_SOURCE); while ((data = aparser.getValue("dasserver", true)) != null) { String nickname = null; String url = null; - boolean seq = false, feat = true; int pos = data.indexOf('='); // determine capabilities if (pos > 0) @@ -966,7 +993,7 @@ public class Jalview + nickname + "|" + url); if (source == null) { - source = new Vector(); + source = new Vector(); } source.addElement(nickname); } @@ -984,7 +1011,7 @@ public class Jalview System.out.println("adding source '" + data + "'"); if (source == null) { - source = new Vector(); + source = new Vector(); } source.addElement(data); } @@ -996,7 +1023,8 @@ public class Jalview * * @param dasSources */ - private static FeatureFetcher startFeatureFetching(final Vector dasSources) + private FeatureFetcher startFeatureFetching( + final Vector dasSources) { FeatureFetcher ff = new FeatureFetcher(); AlignFrame afs[] = Desktop.getAlignFrames(); @@ -1020,176 +1048,37 @@ public class Jalview } return false; } -} - -/** - * Notes: this argParser does not distinguish between parameter switches, - * parameter values and argument text. If an argument happens to be identical to - * a parameter, it will be taken as such (even though it didn't have a '-' - * prefixing it). - * - * @author Andrew Waterhouse and JBP documented. - * - */ - -class rnabuttonlistener implements ActionListener -{ - @Override - public void actionPerformed(ActionEvent arg0) - { - System.out.println("Good idea ! "); - - } -} -class pbuttonlistener implements ActionListener -{ - @Override - public void actionPerformed(ActionEvent arg0) + public AlignFrame[] getAlignFrames() { + return desktop == null ? new AlignFrame[] { getCurrentAlignFrame() } + : Desktop.getAlignFrames(); } -} - -class ArgsParser -{ - Vector vargs = null; - - public ArgsParser(String[] args) - { - vargs = new Vector(); - for (int i = 0; i < args.length; i++) - { - String arg = args[i].trim(); - if (arg.charAt(0) == '-') - { - arg = arg.substring(1); - } - vargs.addElement(arg); - } - } /** - * check for and remove first occurence of arg+parameter in arglist. - * - * @param arg - * @return return the argument following the given arg if arg was in list. + * Quit method delegates to Desktop.quit - unless running in headless mode + * when it just ends the JVM */ - public String getValue(String arg) + public void quit() { - return getValue(arg, false); - } - - public String getValue(String arg, boolean utf8decode) - { - int index = vargs.indexOf(arg); - String dc = null, ret = null; - if (index != -1) - { - ret = vargs.elementAt(index + 1).toString(); - vargs.removeElementAt(index); - vargs.removeElementAt(index); - if (utf8decode && ret != null) - { - try - { - dc = URLDecoder.decode(ret, "UTF-8"); - ret = dc; - } catch (Exception e) - { - // TODO: log failure to decode - } - } - } - return ret; - } - - /** - * check for and remove first occurence of arg in arglist. - * - * @param arg - * @return true if arg was present in argslist. - */ - public boolean contains(String arg) - { - if (vargs.contains(arg)) + if (desktop != null) { - vargs.removeElement(arg); - return true; + desktop.quit(); } else { - return false; + System.exit(0); } } - public String nextValue() - { - return vargs.remove(0).toString(); - } - - public int getSize() + public static AlignFrame getCurrentAlignFrame() { - return vargs.size(); + return Jalview.currentAlignFrame; } -} - -/** - * keep track of feature fetching tasks. - * - * @author JimP - * - */ -class FeatureFetcher -{ - /* - * TODO: generalise to track all jalview events to orchestrate batch - * processing events. - */ - - private int queued = 0; - - private int running = 0; - - public FeatureFetcher() + public static void setCurrentAlignFrame(AlignFrame currentAlignFrame) { - + Jalview.currentAlignFrame = currentAlignFrame; } - - public void addFetcher(final AlignFrame af, final Vector dasSources) - { - final long id = System.currentTimeMillis(); - queued++; - final FeatureFetcher us = this; - new Thread(new Runnable() - { - - @Override - public void run() - { - synchronized (us) - { - queued--; - running++; - } - - af.setProgressBar(MessageManager - .getString("status.das_features_being_retrived"), id); - af.featureSettings_actionPerformed(null); - af.featureSettings.fetchDasFeatures(dasSources, true); - af.setProgressBar(null, id); - synchronized (us) - { - running--; - } - } - }).start(); - } - - public synchronized boolean allFinished() - { - return queued == 0 && running == 0; - } - } diff --git a/src/jalview/bin/JalviewLite.java b/src/jalview/bin/JalviewLite.java index 13e4b7e..b30ad41 100644 --- a/src/jalview/bin/JalviewLite.java +++ b/src/jalview/bin/JalviewLite.java @@ -466,17 +466,11 @@ public class JalviewLite extends Applet implements SequenceI rs = sel.getSequenceAt(0); start = rs.findIndex(start); end = rs.findIndex(end); - if (csel != null) - { - List cs = csel.getSelected(); - // note - the following actually clears cs as well, since - // csel.getSelected returns a reference. Need to check if we need to - // have a concurrentModification exception thrown here - csel.clear(); - for (Integer selectedCol : cs) - { - csel.addElement(rs.findIndex(selectedCol)); - } + List cs = new ArrayList(csel.getSelected()); + csel.clear(); + for (Integer selectedCol : cs) + { + csel.addElement(rs.findIndex(selectedCol)); } } sel.setStartRes(start); diff --git a/src/jalview/controller/AlignViewController.java b/src/jalview/controller/AlignViewController.java index 5ab522e..35a0ba7 100644 --- a/src/jalview/controller/AlignViewController.java +++ b/src/jalview/controller/AlignViewController.java @@ -171,157 +171,136 @@ public class AlignViewController implements AlignViewControllerI // JBPNote this routine could also mark rows, not just columns. // need a decent query structure to allow all types of feature searches BitSet bs = new BitSet(); - int alw, alStart; - SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null ? viewport - .getAlignment() : viewport.getSelectionGroup()); - alStart = sqcol.getStartRes(); - alw = sqcol.getEndRes() + 1; + SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null || extendCurrent) ? viewport + .getAlignment() : viewport.getSelectionGroup(); + + int nseq = findColumnsWithFeature(featureType, sqcol, bs); + + ColumnSelection cs = viewport.getColumnSelection(); + if (cs == null) + { + cs = new ColumnSelection(); + } + + if (bs.cardinality() > 0 || invert) + { + boolean changed = cs.markColumns(bs, sqcol.getStartRes(), + sqcol.getEndRes(), invert, extendCurrent, toggle); + if (changed) + { + viewport.setColumnSelection(cs); + alignPanel.paintAlignment(true); + int columnCount = invert ? (sqcol.getEndRes() - sqcol.getStartRes() + 1) + - bs.cardinality() + : bs.cardinality(); + avcg.setStatus(MessageManager.formatMessage( + "label.view_controller_toggled_marked", + new String[] { + toggle ? MessageManager.getString("label.toggled") + : MessageManager.getString("label.marked"), + String.valueOf(columnCount), + invert ? MessageManager + .getString("label.not_containing") + : MessageManager.getString("label.containing"), + featureType, Integer.valueOf(nseq).toString() })); + return true; + } + } + else + { + avcg.setStatus(MessageManager.formatMessage( + "label.no_feature_of_type_found", + new String[] { featureType })); + if (!extendCurrent) + { + cs.clear(); + alignPanel.paintAlignment(true); + } + } + return false; + } + + /** + * Sets a bit in the BitSet for each column (base 0) in the sequence + * collection which includes the specified feature type. Returns the number of + * sequences which have the feature in the selected range. + * + * @param featureType + * @param sqcol + * @param bs + * @return + */ + static int findColumnsWithFeature(String featureType, + SequenceCollectionI sqcol, BitSet bs) + { + final int startPosition = sqcol.getStartRes() + 1; // converted to base 1 + final int endPosition = sqcol.getEndRes() + 1; List seqs = sqcol.getSequences(); int nseq = 0; for (SequenceI sq : seqs) { - int tfeat = 0; + boolean sequenceHasFeature = false; if (sq != null) { - SequenceFeature[] sf = sq.getSequenceFeatures(); - if (sf != null) + SequenceFeature[] sfs = sq.getSequenceFeatures(); + if (sfs != null) { + /* + * check whether the feature start/end (base 1) + * overlaps the selection start/end + */ int ist = sq.findIndex(sq.getStart()); int iend = sq.findIndex(sq.getEnd()); - if (iend < alStart || ist > alw) + if (iend < startPosition || ist > endPosition) { // sequence not in region continue; } - for (SequenceFeature sfpos : sf) + for (SequenceFeature sf : sfs) { - // future functionalty - featureType == null means mark columns + // future functionality - featureType == null means mark columns // containing all displayed features - if (sfpos != null && (featureType.equals(sfpos.getType()))) + if (sf != null && (featureType.equals(sf.getType()))) { - tfeat++; // optimisation - could consider 'spos,apos' like cursor argument // - findIndex wastes time by starting from first character and // counting - int i = sq.findIndex(sfpos.getBegin()); - int j = sq.findIndex(sfpos.getEnd()); - if (j < alStart || i > alw) + int i = sq.findIndex(sf.getBegin()); + int j = sq.findIndex(sf.getEnd()); + if (j < startPosition || i > endPosition) { // feature is outside selected region continue; } - if (i < alStart) + sequenceHasFeature = true; + if (i < startPosition) { - i = alStart; + i = startPosition; } if (i < ist) { i = ist; } - if (j > alw) + if (j > endPosition) { - j = alw; + j = endPosition; } for (; i <= j; i++) { - bs.set(i - 1); + bs.set(i - 1); // convert to base 0 } } } } - if (tfeat > 0) + if (sequenceHasFeature) { nseq++; } } } - ColumnSelection cs = viewport.getColumnSelection(); - if (bs.cardinality() > 0 || invert) - { - boolean changed = false; - if (cs == null) - { - cs = new ColumnSelection(); - } - else - { - if (!extendCurrent) - { - changed = !cs.isEmpty(); - cs.clear(); - } - } - if (invert) - { - // invert only in the currently selected sequence region - for (int i = bs.nextClearBit(alStart), ibs = bs.nextSetBit(alStart); i >= alStart - && i < (alw);) - { - if (ibs < 0 || i < ibs) - { - changed = true; - if (toggle && cs.contains(i)) - { - cs.removeElement(i++); - } - else - { - cs.addElement(i++); - } - } - else - { - i = bs.nextClearBit(ibs); - ibs = bs.nextSetBit(i); - } - } - } - else - { - for (int i = bs.nextSetBit(alStart); i >= alStart; i = bs - .nextSetBit(i + 1)) - { - changed = true; - if (toggle && cs.contains(i)) - { - cs.removeElement(i); - } - else - { - cs.addElement(i); - } - } - } - 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 - { - avcg.setStatus(MessageManager.formatMessage( - "label.no_feature_of_type_found", - new String[] { featureType })); - if (!extendCurrent && cs != null) - { - cs.clear(); - alignPanel.paintAlignment(true); - } - } - return false; + return nseq; } @Override diff --git a/src/jalview/datamodel/AlignedCodonFrame.java b/src/jalview/datamodel/AlignedCodonFrame.java index 6d6cdb5..326cc4e 100644 --- a/src/jalview/datamodel/AlignedCodonFrame.java +++ b/src/jalview/datamodel/AlignedCodonFrame.java @@ -23,6 +23,7 @@ package jalview.datamodel; import jalview.util.MapList; import jalview.util.MappingUtils; +import java.util.AbstractList; import java.util.ArrayList; import java.util.List; @@ -36,7 +37,7 @@ public class AlignedCodonFrame /* * Data bean to hold mappings from one sequence to another */ - private class SequenceToSequenceMapping + public class SequenceToSequenceMapping { private SequenceI fromSeq; @@ -57,6 +58,54 @@ public class AlignedCodonFrame return String.format("From %s %s", fromSeq.getName(), mapping.toString()); } + + /** + * Returns a hashCode derived from the hashcodes of the mappings and fromSeq + * + * @see SequenceToSequenceMapping#hashCode() + */ + @Override + public int hashCode() + { + return (fromSeq == null ? 0 : fromSeq.hashCode() * 31) + + mapping.hashCode(); + } + + /** + * Answers true if the objects hold the same mapping between the same two + * sequences + * + * @see Mapping#equals + */ + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof SequenceToSequenceMapping)) + { + return false; + } + SequenceToSequenceMapping that = (SequenceToSequenceMapping) obj; + if (this.mapping == null) + { + return that.mapping == null; + } + // TODO: can simplify by asserting fromSeq is a dataset sequence + return (this.fromSeq == that.fromSeq || (this.fromSeq != null + && that.fromSeq != null + && this.fromSeq.getDatasetSequence() != null && this.fromSeq + .getDatasetSequence() == that.fromSeq + .getDatasetSequence())) && this.mapping.equals(that.mapping); + } + + public SequenceI getFromSeq() + { + return fromSeq; + } + + public Mapping getMapping() + { + return mapping; + } } private List mappings; @@ -79,6 +128,21 @@ public class AlignedCodonFrame */ public void addMap(SequenceI dnaseq, SequenceI aaseq, MapList map) { + addMap(dnaseq, aaseq, map, null); + } + + /** + * Adds a mapping between the dataset sequences for the associated dna and + * protein sequence objects + * + * @param dnaseq + * @param aaseq + * @param map + * @param mapFromId + */ + public void addMap(SequenceI dnaseq, SequenceI aaseq, MapList map, + String mapFromId) + { // JBPNote DEBUG! THIS ! // dnaseq.transferAnnotation(aaseq, mp); // aaseq.transferAnnotation(dnaseq, new Mapping(map.getInverse())); @@ -90,6 +154,8 @@ public class AlignedCodonFrame /* * if we already hold a mapping between these sequences, just add to it + * note that 'adding' a duplicate map does nothing; this protects against + * creating duplicate mappings in AlignedCodonFrame */ for (SequenceToSequenceMapping ssm : mappings) { @@ -104,6 +170,7 @@ public class AlignedCodonFrame * otherwise, add a new sequence mapping */ Mapping mp = new Mapping(toSeq, map); + mp.setMappedFromId(mapFromId); mappings.add(new SequenceToSequenceMapping(fromSeq, mp)); } @@ -421,7 +488,8 @@ public class AlignedCodonFrame for (SequenceToSequenceMapping ssm : mappings) { - if (ssm.mapping.to == protein) + if (ssm.mapping.to == protein + && ssm.mapping.getMap().getFromRatio() == 3) { ml = ssm.mapping.map; dnaSeq = ssm.fromSeq; @@ -651,7 +719,7 @@ public class AlignedCodonFrame } /** - * Returns the first mapping found that is from 'fromSeq' to 'toSeq', or null + * Returns the first mapping found that is between 'fromSeq' and 'toSeq', or null * if none found * * @param fromSeq @@ -662,16 +730,54 @@ public class AlignedCodonFrame */ public Mapping getMappingBetween(SequenceI fromSeq, SequenceI toSeq) { + SequenceI dssFrom = fromSeq.getDatasetSequence() == null ? fromSeq + : fromSeq.getDatasetSequence(); + SequenceI dssTo = toSeq.getDatasetSequence() == null ? toSeq : toSeq + .getDatasetSequence(); + for (SequenceToSequenceMapping mapping : mappings) { SequenceI from = mapping.fromSeq; SequenceI to = mapping.mapping.to; - if ((from == fromSeq || from == fromSeq.getDatasetSequence()) - && (to == toSeq || to == toSeq.getDatasetSequence())) + if ((from == dssFrom && to == dssTo) + || (from == dssTo && to == dssFrom)) { return mapping.mapping; } } return null; } + + /** + * Returns a hashcode derived from the list of sequence mappings + * + * @see SequenceToSequenceMapping#hashCode() + * @see AbstractList#hashCode() + */ + @Override + public int hashCode() + { + return this.mappings.hashCode(); + } + + /** + * Two AlignedCodonFrame objects are equal if they hold the same ordered list + * of mappings + * + * @see SequenceToSequenceMapping# + */ + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof AlignedCodonFrame)) + { + return false; + } + return this.mappings.equals(((AlignedCodonFrame) obj).mappings); + } + + public List getMappings() + { + return mappings; + } } diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index a9b0d53..2f64759 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -21,6 +21,7 @@ package jalview.datamodel; import jalview.analysis.AlignmentUtils; +import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping; import jalview.io.FastaFile; import jalview.util.Comparison; import jalview.util.MessageManager; @@ -44,7 +45,7 @@ import java.util.Vector; */ public class Alignment implements AlignmentI { - protected Alignment dataset; + private Alignment dataset; protected List sequences; @@ -109,7 +110,10 @@ public class Alignment implements AlignmentI /* * Share the same dataset sequence mappings (if any). */ - this.setCodonFrames(al.getCodonFrames()); + if (dataset == null && al.getDataset() == null) + { + this.setCodonFrames(al.getCodonFrames()); + } } /** @@ -222,18 +226,21 @@ public class Alignment implements AlignmentI { if (dataset != null) { + // maintain dataset integrity - if (snew.getDatasetSequence() != null) - { - getDataset().addSequence(snew.getDatasetSequence()); - } - else + SequenceI dsseq = snew.getDatasetSequence(); + if (dsseq == null) { // derive new sequence SequenceI adding = snew.deriveSequence(); - getDataset().addSequence(adding.getDatasetSequence()); snew = adding; + dsseq = snew.getDatasetSequence(); + } + if (getDataset().findIndex(dsseq) == -1) + { + getDataset().addSequence(dsseq); } + } if (sequences == null) { @@ -252,18 +259,22 @@ public class Alignment implements AlignmentI } } - /** - * Adds a sequence to the alignment. Recalculates maxLength and size. - * - * @param snew - */ @Override - public void setSequenceAt(int i, SequenceI snew) + public SequenceI replaceSequenceAt(int i, SequenceI snew) { synchronized (sequences) { - deleteSequence(i); - sequences.set(i, snew); + if (sequences.size() > i) + { + return sequences.set(i, snew); + + } + else + { + sequences.add(snew); + hiddenSequences.adjustHeightSequenceAdded(); + } + return null; } } @@ -986,7 +997,7 @@ public class Alignment implements AlignmentI } @Override - public void setDataset(Alignment data) + public void setDataset(AlignmentI data) { if (dataset == null && data == null) { @@ -994,7 +1005,12 @@ public class Alignment implements AlignmentI } else if (dataset == null && data != null) { - dataset = data; + if (!(data instanceof Alignment)) + { + throw new Error( + "Implementation Error: jalview.datamodel.Alignment does not yet support other implementations of AlignmentI as its dataset reference"); + } + dataset = (Alignment) data; for (int i = 0; i < getHeight(); i++) { SequenceI currentSeq = getSequenceAt(i); @@ -1021,6 +1037,62 @@ public class Alignment implements AlignmentI } /** + * add dataset sequences to seq for currentSeq and any sequences it references + */ + private void resolveAndAddDatasetSeq(SequenceI currentSeq, + Set seqs, boolean createDatasetSequence) + { + if (currentSeq.getDatasetSequence() != null) + { + currentSeq = currentSeq.getDatasetSequence(); + } + else + { + if (createDatasetSequence) + { + currentSeq = currentSeq.createDatasetSequence(); + } + } + if (seqs.contains(currentSeq)) + { + return; + } + List toProcess = new ArrayList(); + toProcess.add(currentSeq); + while (toProcess.size() > 0) + { + // use a queue ? + SequenceI curDs = toProcess.remove(0); + if (seqs.contains(curDs)) + { + continue; + } + seqs.add(curDs); + // iterate over database references, making sure we add forward referenced + // sequences + if (curDs.getDBRefs() != null) + { + for (DBRefEntry dbr : curDs.getDBRefs()) + { + if (dbr.getMap() != null && dbr.getMap().getTo() != null) + { + if (dbr.getMap().getTo().getDatasetSequence() != null) + { + throw new Error("Implementation error: Map.getTo() for dbref" + + dbr + " is not a dataset sequence."); + // TODO: if this happens, could also rewrite the reference to + // point to new dataset sequence + } + // we recurse to add all forward references to dataset sequences via + // DBRefs/etc + toProcess.add(dbr.getMap().getTo()); + } + } + } + } + } + + /** * Creates a new dataset for this alignment. Can only be done once - if * dataset is not null this will not be performed. */ @@ -1030,22 +1102,32 @@ public class Alignment implements AlignmentI { return; } - SequenceI[] seqs = new SequenceI[getHeight()]; - SequenceI currentSeq; + // try to avoid using SequenceI.equals at this stage, it will be expensive + Set seqs = new jalview.util.LinkedIdentityHashSet(); + for (int i = 0; i < getHeight(); i++) { - currentSeq = getSequenceAt(i); - if (currentSeq.getDatasetSequence() != null) - { - seqs[i] = currentSeq.getDatasetSequence(); - } - else + SequenceI currentSeq = getSequenceAt(i); + resolveAndAddDatasetSeq(currentSeq, seqs, true); + } + + // verify all mappings are in dataset + for (AlignedCodonFrame cf : codonFrameList) + { + for (SequenceToSequenceMapping ssm : cf.getMappings()) { - seqs[i] = currentSeq.createDatasetSequence(); + if (!seqs.contains(ssm.getFromSeq())) + { + resolveAndAddDatasetSeq(ssm.getFromSeq(), seqs, false); + } + if (!seqs.contains(ssm.getMapping().getTo())) + { + resolveAndAddDatasetSeq(ssm.getMapping().getTo(), seqs, false); + } } } - - dataset = new Alignment(seqs); + // finally construct dataset + dataset = new Alignment(seqs.toArray(new SequenceI[seqs.size()])); // move mappings to the dataset alignment dataset.codonFrameList = this.codonFrameList; this.codonFrameList = null; @@ -1340,6 +1422,10 @@ public class Alignment implements AlignmentI @Override public List getCodonFrames() { + // TODO: Fix this method to fix failing AlignedCodonFrame tests + // this behaviour is currently incorrect. method should return codon frames + // for just the alignment, + // selected from dataset return dataset != null ? dataset.getCodonFrames() : codonFrameList; } @@ -1361,11 +1447,7 @@ public class Alignment implements AlignmentI @Override public void append(AlignmentI toappend) { - if (toappend == this) - { - System.err.println("Self append may cause a deadlock."); - } - // TODO test this method for a future 2.5 release + // TODO JAL-1270 needs test coverage // currently tested for use in jalview.gui.SequenceFetcher boolean samegap = toappend.getGapCharacter() == getGapCharacter(); char oldc = toappend.getGapCharacter(); @@ -1376,6 +1458,8 @@ public class Alignment implements AlignmentI .getFullAlignment().getSequences() : toappend.getSequences(); if (sqs != null) { + // avoid self append deadlock by + List toappendsq = new ArrayList(); synchronized (sqs) { for (SequenceI addedsq : sqs) @@ -1391,9 +1475,13 @@ public class Alignment implements AlignmentI } } } - addSequence(addedsq); + toappendsq.add(addedsq); } } + for (SequenceI addedsq : toappendsq) + { + addSequence(addedsq); + } } AlignmentAnnotation[] alan = toappend.getAlignmentAnnotation(); for (int a = 0; alan != null && a < alan.length; a++) @@ -1401,6 +1489,7 @@ public class Alignment implements AlignmentI addAnnotation(alan[a]); } + // use add method getCodonFrames().addAll(toappend.getCodonFrames()); List sg = toappend.getGroups(); @@ -1691,9 +1780,11 @@ public class Alignment implements AlignmentI * Parameters control whether gaps in exon (mapped) and intron (unmapped) * regions are preserved. Gaps that connect introns to exons are treated * conservatively, i.e. only preserved if both intron and exon gaps are - * preserved. + * preserved. TODO: check caveats below where the implementation fails * * @param al + * - must have same dataset, and sequences in al must have equivalent + * dataset sequence and start/end bounds under given mapping * @param preserveMappedGaps * if true, gaps within and between mapped codons are preserved * @param preserveUnmappedGaps @@ -1704,12 +1795,17 @@ public class Alignment implements AlignmentI boolean preserveUnmappedGaps) { // TODO should this method signature be the one in the interface? + // JBPComment - yes - neither flag is used, so should be deleted. boolean thisIsNucleotide = this.isNucleotide(); boolean thatIsProtein = !al.isNucleotide(); if (!thatIsProtein && !thisIsNucleotide) { return AlignmentUtils.alignProteinAsDna(this, al); } + else if (thatIsProtein && thisIsNucleotide) + { + return AlignmentUtils.alignCdsAsProtein(this, al); + } return AlignmentUtils.alignAs(this, al); } diff --git a/src/jalview/datamodel/AlignmentAnnotation.java b/src/jalview/datamodel/AlignmentAnnotation.java index 7990a5c..2a89fa1 100755 --- a/src/jalview/datamodel/AlignmentAnnotation.java +++ b/src/jalview/datamodel/AlignmentAnnotation.java @@ -24,11 +24,11 @@ import jalview.analysis.Rna; import jalview.analysis.SecStrConsensus.SimpleBP; import jalview.analysis.WUSSParseException; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -79,7 +79,7 @@ public class AlignmentAnnotation /** Array of annotations placed in the current coordinate system */ public Annotation[] annotations; - public ArrayList bps = null; + public List bps = null; /** * RNA secondary structure contact positions @@ -102,8 +102,8 @@ public class AlignmentAnnotation { try { - _rnasecstr = Rna.GetBasePairs(RNAannot); - bps = Rna.GetModeleBP(RNAannot); + bps = Rna.getModeleBP(RNAannot); + _rnasecstr = Rna.getBasePairs(bps); invalidrnastruc = -1; } catch (WUSSParseException px) { @@ -272,7 +272,7 @@ public class AlignmentAnnotation // JBPNote: what does this do ? public void ConcenStru(CharSequence RNAannot) throws WUSSParseException { - bps = Rna.GetModeleBP(RNAannot); + bps = Rna.getModeleBP(RNAannot); } /** @@ -485,7 +485,7 @@ public class AlignmentAnnotation this(0, annotations.length); } - public AnnotCharSequence(int start, int end) + AnnotCharSequence(int start, int end) { offset = start; max = end; @@ -593,6 +593,7 @@ public class AlignmentAnnotation if (annotations == null) { visible = false; // try to prevent renderer from displaying. + invalidrnastruc = -1; return; // this is a non-annotation row annotation - ie a sequence score. } @@ -1411,6 +1412,77 @@ public class AlignmentAnnotation this.annotationId = ANNOTATION_ID_PREFIX + Long.toString(nextId()); } + /** + * Returns the match for the last unmatched opening RNA helix pair symbol + * preceding the given column, or '(' if nothing found to match. + * + * @param column + * @return + */ + public String getDefaultRnaHelixSymbol(int column) + { + String result = "("; + if (annotations == null) + { + return result; + } + + /* + * for each preceding column, if it contains an open bracket, + * count whether it is still unmatched at column, if so return its pair + * (likely faster than the fancy alternative using stacks) + */ + for (int col = column - 1; col >= 0; col--) + { + Annotation annotation = annotations[col]; + if (annotation == null) + { + continue; + } + String displayed = annotation.displayCharacter; + if (displayed == null || displayed.length() != 1) + { + continue; + } + char symbol = displayed.charAt(0); + if (!Rna.isOpeningParenthesis(symbol)) + { + continue; + } + + /* + * found an opening bracket symbol + * count (closing-opening) symbols of this type that follow it, + * up to and excluding the target column; if the count is less + * than 1, the opening bracket is unmatched, so return its match + */ + String closer = String.valueOf(Rna + .getMatchingClosingParenthesis(symbol)); + String opener = String.valueOf(symbol); + int count = 0; + for (int j = col + 1; j < column; j++) + { + if (annotations[j] != null) + { + String s = annotations[j].displayCharacter; + if (closer.equals(s)) + { + count++; + } + else if (opener.equals(s)) + { + count--; + } + } + } + if (count < 1) + { + return closer; + } + } + return result; + } + protected static synchronized long nextId() { return counter++; diff --git a/src/jalview/datamodel/AlignmentI.java b/src/jalview/datamodel/AlignmentI.java index 76d1a48..1d37fa6 100755 --- a/src/jalview/datamodel/AlignmentI.java +++ b/src/jalview/datamodel/AlignmentI.java @@ -41,7 +41,8 @@ public interface AlignmentI extends AnnotatedCollectionI * * Calculates the maximum width of the alignment, including gaps. * - * @return Greatest sequence length within alignment. + * @return Greatest sequence length within alignment, or -1 if no sequences + * present */ @Override int getWidth(); @@ -107,11 +108,14 @@ public interface AlignmentI extends AnnotatedCollectionI * Used to set a particular index of the alignment with the given sequence. * * @param i - * Index of sequence to be updated. + * Index of sequence to be updated. if i>length, sequence will be + * added to end, with no intervening positions. * @param seq - * New sequence to be inserted. + * New sequence to be inserted. The existing sequence at position i + * will be replaced. + * @return existing sequence (or null if i>current length) */ - void setSequenceAt(int i, SequenceI seq); + SequenceI replaceSequenceAt(int i, SequenceI seq); /** * Deletes a sequence from the alignment @@ -305,7 +309,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @return Alignment containing dataset sequences or null of this is a * dataset. */ - Alignment getDataset(); + AlignmentI getDataset(); /** * Set the associated dataset for the alignment, or create one. @@ -313,7 +317,7 @@ public interface AlignmentI extends AnnotatedCollectionI * @param dataset * The dataset alignment or null to construct one. */ - void setDataset(Alignment dataset); + void setDataset(AlignmentI dataset); /** * pads sequences with gaps (to ensure the set looks like an alignment) diff --git a/src/jalview/datamodel/AlignmentView.java b/src/jalview/datamodel/AlignmentView.java index 756a116..9db9f38 100644 --- a/src/jalview/datamodel/AlignmentView.java +++ b/src/jalview/datamodel/AlignmentView.java @@ -26,7 +26,6 @@ import jalview.util.ShiftList; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; -import java.util.Vector; /** * Transient object compactly representing a 'view' of an alignment - with @@ -69,13 +68,51 @@ public class AlignmentView */ private class ScGroup { - public Vector seqs; + public List seqs; public SequenceGroup sg; ScGroup() { - seqs = new Vector(); + seqs = new ArrayList(); + } + + /** + * @param seq + * @return true if seq was not a member before and was added to group + */ + public boolean add(SeqCigar seq) + { + if (!seq.isMemberOf(this)) + { + seqs.add(seq); + seq.setGroupMembership(this); + return true; + } + else + { + return false; + } + } + + /** + * + * @param seq + * @return true if seq was a member and was removed from group + */ + public boolean remove(SeqCigar seq) + { + if (seq.removeGroupMembership(this)) + { + seqs.remove(seq); + return true; + } + return false; + } + + public int size() + { + return seqs.size(); } } @@ -83,7 +120,7 @@ public class AlignmentView * vector of selected seqCigars. This vector is also referenced by each * seqCigar contained in it. */ - private Vector selected; + private ScGroup selected; /** * Construct an alignmentView from a live jalview alignment view. Note - @@ -124,7 +161,7 @@ public class AlignmentView if (selection != null && selection.getSize() > 0) { List sel = selection.getSequences(null); - this.selected = new Vector(); + this.selected = new ScGroup(); selseqs = selection .getSequencesInOrder(alignment, selectedRegionOnly); } @@ -194,8 +231,7 @@ public class AlignmentView if (selection != null && selection.getSize() > 0 && !selectedRegionOnly) { - sequences[csi].setGroupMembership(selected); - selected.addElement(sequences[csi]); + selected.add(sequences[csi]); } if (seqsets != null) { @@ -203,9 +239,8 @@ public class AlignmentView { if ((seqsets.get(sg)).contains(selseqs[i])) { - sequences[csi].setGroupMembership(sgrps[sg]); sgrps[sg].sg.deleteSequence(selseqs[i], false); - sgrps[sg].seqs.addElement(sequences[csi]); + sgrps[sg].add(sequences[csi]); if (!addedgps[sg]) { if (scGroups == null) @@ -242,8 +277,7 @@ public class AlignmentView if (!seqcigararray.isSeqCigarArray()) { throw new Error( - MessageManager - .getString("error.implementation_error_can_only_make_alignmnet_from_cigararray")); + "Implementation Error - can only make an alignment view from a CigarArray of sequences."); } // contigs = seqcigararray.applyDeletions(); contigs = seqcigararray.getDeletedRegions(); @@ -1073,10 +1107,10 @@ public class AlignmentView + sgr.sg.getEndRes()); for (int s = 0; s < sgr.seqs.size(); s++) { - if (!((SeqCigar) sgr.seqs.elementAt(s)).isMemberOf(sgr)) + // JBPnote this should be a unit test for ScGroup + if (!sgr.seqs.get(s).isMemberOf(sgr)) { - os.println("** WARNING: sequence " - + ((SeqCigar) sgr.seqs.elementAt(s)).toString() + os.println("** WARNING: sequence " + sgr.seqs.get(s).toString() + " is not marked as member of group."); } } diff --git a/src/jalview/datamodel/CigarBase.java b/src/jalview/datamodel/CigarBase.java index bcf8596..5fb507a 100644 --- a/src/jalview/datamodel/CigarBase.java +++ b/src/jalview/datamodel/CigarBase.java @@ -533,6 +533,7 @@ public abstract class CigarBase { case M: cursor += range[i]; + break; case I: vcursor += range[i]; break; diff --git a/src/jalview/datamodel/ColumnSelection.java b/src/jalview/datamodel/ColumnSelection.java index e3a8472..8bc8f54 100644 --- a/src/jalview/datamodel/ColumnSelection.java +++ b/src/jalview/datamodel/ColumnSelection.java @@ -32,28 +32,65 @@ 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 { /* * list of selected columns (ordered by selection order, not column order) */ - private List order = new ArrayList(); + private List order; + + /* + * an unmodifiable view of the selected columns list + */ + private List _uorder; /** * bitfield for column selection - allows quick lookup */ - private BitSet selected = new BitSet(); + private BitSet selected; + + /** + * Constructor + */ + IntList() + { + order = new ArrayList(); + _uorder = Collections.unmodifiableList(order); + selected = new BitSet(); + } + + /** + * Copy constructor + * + * @param other + */ + IntList(IntList other) + { + this(); + if (other != null) + { + int j = other.size(); + for (int i = 0; i < j; i++) + { + add(other.elementAt(i)); + } + } + } /** * adds a new column i to the selection - only if i is not already selected * * @param i */ - public void add(int i) + void add(int i) { if (!selected.get(i)) { @@ -62,13 +99,13 @@ public class ColumnSelection } } - public void clear() + void clear() { order.clear(); selected.clear(); } - public void remove(int col) + void remove(int col) { Integer colInt = new Integer(col); @@ -83,22 +120,27 @@ public class ColumnSelection } } - public boolean contains(Integer colInt) + boolean contains(Integer colInt) { return selected.get(colInt); } - public boolean isEmpty() + boolean isEmpty() { return order.isEmpty(); } - public List getList() + /** + * Returns a read-only view of the selected columns list + * + * @return + */ + List getList() { - return order; + return _uorder; } - public int size() + int size() { return order.size(); } @@ -109,7 +151,7 @@ public class ColumnSelection * @param i * @return */ - public int elementAt(int i) + int elementAt(int i) { return order.get(i); } @@ -152,7 +194,7 @@ public class ColumnSelection * @param change * - delta for shift */ - public void compensateForEdits(int start, int change) + void compensateForEdits(int start, int change) { BitSet mask = new BitSet(); for (int i = 0; i < order.size(); i++) @@ -171,17 +213,17 @@ public class ColumnSelection selected.or(mask); } - public boolean isSelected(int column) + boolean isSelected(int column) { return selected.get(column); } - public int getMaxColumn() + int getMaxColumn() { return selected.length() - 1; } - public int getMinColumn() + int getMinColumn() { return selected.get(0) ? 0 : selected.nextSetBit(0); } @@ -189,7 +231,7 @@ public class ColumnSelection /** * @return a series of selection intervals along the range */ - public List getRanges() + List getRanges() { List rlist = new ArrayList(); if (selected.isEmpty()) @@ -205,9 +247,26 @@ 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 @@ -223,7 +282,7 @@ public class ColumnSelection */ public void addElement(int col) { - selected.add(col); + selection.add(col); } /** @@ -231,7 +290,7 @@ public class ColumnSelection */ public void clear() { - selected.clear(); + selection.clear(); } /** @@ -242,7 +301,7 @@ public class ColumnSelection */ public void removeElement(int col) { - selected.remove(col); + selection.remove(col); } /** @@ -259,21 +318,26 @@ 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); } } } /** - * Returns a list of selected columns. The list contains no duplicates but is - * not necessarily ordered. It also may include columns hidden from the - * current view + * Returns a read-only view of the (possibly empty) list of selected columns + *

        + * The list contains no duplicates but is not necessarily ordered. It also may + * include columns hidden from the current view. To modify (for example sort) + * the list, you should first make a copy. + *

        + * The list is not thread-safe: iterating over it could result in + * ConcurrentModificationException if it is modified by another thread. */ public List getSelected() { - return selected.getList(); + return selection.getList(); } /** @@ -282,7 +346,7 @@ public class ColumnSelection */ public List getSelectedRanges() { - return selected.getRanges(); + return selection.getRanges(); } /** @@ -294,7 +358,7 @@ public class ColumnSelection */ public boolean contains(int col) { - return (col > -1) ? selected.isSelected(col) : false; + return (col > -1) ? selection.isSelected(col) : false; } /** @@ -302,7 +366,7 @@ public class ColumnSelection */ public boolean isEmpty() { - return selected == null || selected.isEmpty(); + return selection == null || selection.isEmpty(); } /** @@ -312,11 +376,11 @@ public class ColumnSelection */ public int getMax() { - if (selected.isEmpty()) + if (selection.isEmpty()) { return -1; } - return selected.getMaxColumn(); + return selection.getMaxColumn(); } /** @@ -326,11 +390,11 @@ public class ColumnSelection */ public int getMin() { - if (selected.isEmpty()) + if (selection.isEmpty()) { return 1000000000; } - return selected.getMinColumn(); + return selection.getMinColumn(); } /** @@ -344,7 +408,7 @@ public class ColumnSelection public List compensateForEdit(int start, int change) { List deletedHiddenColumns = null; - selected.compensateForEdits(start, change); + selection.compensateForEdits(start, change); if (hiddenColumns != null) { @@ -394,7 +458,7 @@ public class ColumnSelection private void compensateForDelEdits(int start, int change) { - selected.compensateForEdits(start, change); + selection.compensateForEdits(start, change); if (hiddenColumns != null) { @@ -571,12 +635,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. @@ -600,7 +664,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) @@ -743,13 +807,13 @@ public class ColumnSelection public void hideSelectedColumns() { - synchronized (selected) + synchronized (selection) { - for (int[] selregions : selected.getRanges()) + for (int[] selregions : selection.getRanges()) { hideColumns(selregions[0], selregions[1]); } - selected.clear(); + selection.clear(); } } @@ -927,14 +991,7 @@ public class ColumnSelection { if (copy != null) { - if (copy.selected != null) - { - selected = new IntList(); - for (int i = 0, j = copy.selected.size(); i < j; i++) - { - selected.add(copy.selected.elementAt(i)); - } - } + selection = new IntList(copy.selection); if (copy.hiddenColumns != null) { hiddenColumns = new Vector(copy.hiddenColumns.size()); @@ -964,7 +1021,7 @@ public class ColumnSelection SequenceI[] seqs) { int i, iSize = seqs.length; - String selection[] = new String[iSize]; + String selections[] = new String[iSize]; if (hiddenColumns != null && hiddenColumns.size() > 0) { for (i = 0; i < iSize; i++) @@ -1006,18 +1063,18 @@ public class ColumnSelection visibleSeq.append(seqs[i].getSequence(blockStart, end)); } - selection[i] = visibleSeq.toString(); + selections[i] = visibleSeq.toString(); } } else { for (i = 0; i < iSize; i++) { - selection[i] = seqs[i].getSequenceAsString(start, end); + selections[i] = seqs[i].getSequenceAsString(start, end); } } - return selection; + return selections; } /** @@ -1307,7 +1364,7 @@ public class ColumnSelection { if (hiddenColumns != null && isVisible(col.intValue())) { - selected.add(col); + selection.add(col); } } } @@ -1321,8 +1378,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) { @@ -1489,7 +1546,7 @@ public class ColumnSelection */ public boolean hasSelectedColumns() { - return (selected != null && selected.size() > 0); + return (selection != null && selection.size() > 0); } /** @@ -1612,4 +1669,148 @@ public class ColumnSelection 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; + } + + /** + * Answers true if comparing to a ColumnSelection with the same selected + * columns and hidden columns, else false + */ + @Override + public boolean equals(Object obj) + { + if (!(obj instanceof ColumnSelection)) + { + return false; + } + ColumnSelection that = (ColumnSelection) obj; + + /* + * check columns selected are either both null, or match + */ + if (this.selection == null) + { + if (that.selection != null) + { + return false; + } + } + if (!this.selection.equals(that.selection)) + { + return false; + } + + /* + * check hidden columns are either both null, or match + */ + if (this.hiddenColumns == null) + { + return (that.hiddenColumns == null); + } + if (that.hiddenColumns == null + || that.hiddenColumns.size() != this.hiddenColumns.size()) + { + return false; + } + int i = 0; + for (int[] thisRange : hiddenColumns) + { + int[] thatRange = that.hiddenColumns.get(i++); + if (thisRange[0] != thatRange[0] || thisRange[1] != thatRange[1]) + { + return false; + } + } + return true; + } + + /** + * Updates the column selection depending on the parameters, and returns true + * if any change was made to the selection + * + * @param markedColumns + * a set identifying marked columns (base 0) + * @param startCol + * the first column of the range to operate over (base 0) + * @param endCol + * the last column of the range to operate over (base 0) + * @param invert + * if true, deselect marked columns and select unmarked + * @param extendCurrent + * if true, extend rather than replacing the current column selection + * @param toggle + * if true, toggle the selection state of marked columns + * + * @return + */ + public boolean markColumns(BitSet markedColumns, int startCol, + int endCol, boolean invert, boolean extendCurrent, boolean toggle) + { + boolean changed = false; + if (!extendCurrent && !toggle) + { + changed = !this.isEmpty(); + clear(); + } + if (invert) + { + // invert only in the currently selected sequence region + int i = markedColumns.nextClearBit(startCol); + int ibs = markedColumns.nextSetBit(startCol); + while (i >= startCol && i <= endCol) + { + if (ibs < 0 || i < ibs) + { + changed = true; + if (toggle && contains(i)) + { + removeElement(i++); + } + else + { + addElement(i++); + } + } + else + { + i = markedColumns.nextClearBit(ibs); + ibs = markedColumns.nextSetBit(i); + } + } + } + else + { + int i = markedColumns.nextSetBit(startCol); + while (i >= startCol && i <= endCol) + { + changed = true; + if (toggle && contains(i)) + { + removeElement(i); + } + else + { + addElement(i); + } + i = markedColumns.nextSetBit(i + 1); + } + } + return changed; + } + } diff --git a/src/jalview/datamodel/DBRefEntry.java b/src/jalview/datamodel/DBRefEntry.java index 53642b5..ec6dcf8 100755 --- a/src/jalview/datamodel/DBRefEntry.java +++ b/src/jalview/datamodel/DBRefEntry.java @@ -22,11 +22,13 @@ package jalview.datamodel; import jalview.api.DBRefEntryI; +import java.util.Arrays; +import java.util.List; + public class DBRefEntry implements DBRefEntryI { String source = "", version = "", accessionId = ""; - private int startRes, endRes; /** * maps from associated sequence to the database sequence's coordinate system */ @@ -37,7 +39,6 @@ public class DBRefEntry implements DBRefEntryI } - public DBRefEntry(String source, String version, String accessionId) { this(source, version, accessionId, null); @@ -98,6 +99,86 @@ public class DBRefEntry implements DBRefEntryI } /** + * Answers true if this object is either equivalent to, or can be 'improved' + * by, the given entry. Specifically, answers true if + *

          + *
        • source and accession are identical (ignoring case)
        • + *
        • version is identical (ignoring case), or this version is of the format + * "someSource:0", in which case the version for the other entry replaces it
        • + *
        • mappings are not compared but if this entry has no mapping, replace + * with that for the other entry
        • + *
        + * + * @param other + * @return + */ + @Override + public boolean updateFrom(DBRefEntryI other) + { + if (other == null) + { + return false; + } + if (other == this) + { + return true; + } + + /* + * source must either match or be both null + */ + String otherSource = other.getSource(); + if ((source == null && otherSource != null) + || (source != null && otherSource == null) + || (source != null && !source.equalsIgnoreCase(otherSource))) + { + return false; + } + + /* + * accession id must either match or be both null + */ + String otherAccession = other.getAccessionId(); + if ((accessionId == null && otherAccession != null) + || (accessionId != null && otherAccession == null) + || (accessionId != null && !accessionId + .equalsIgnoreCase(otherAccession))) + { + return false; + } + + /* + * if my version is null, "0" or "source:0" then replace with other version, + * otherwise the versions have to match + */ + String otherVersion = other.getVersion(); + + if ((version == null || version.equals("0") || version.endsWith(":0")) + && otherVersion != null) + { + setVersion(otherVersion); + } + else + { + if (version != null + && (otherVersion == null || !version + .equalsIgnoreCase(otherVersion))) + { + return false; + } + } + + /* + * if I have no mapping, take that of the other dbref + */ + if (map == null) + { + setMap(other.getMap()); + } + return true; + } + + /** * test for similar DBRef attributes, except for the map object. * * @param entry @@ -106,6 +187,7 @@ public class DBRefEntry implements DBRefEntryI @Override public boolean equalRef(DBRefEntryI entry) { + // TODO is this method and equals() not needed? if (entry == null) { return false; @@ -145,28 +227,24 @@ public class DBRefEntry implements DBRefEntryI return accessionId; } - @Override public void setAccessionId(String accessionId) { this.accessionId = accessionId; } - @Override public void setSource(String source) { this.source = source; } - @Override public void setVersion(String version) { this.version = version; } - @Override public Mapping getMap() { @@ -204,26 +282,54 @@ public class DBRefEntry implements DBRefEntryI } @Override - public int getStartRes() + public boolean isPrimaryCandidate() { - return startRes; - } - - @Override - public void setStartRes(int startRes) - { - this.startRes = startRes; - } - - @Override - public int getEndRes() - { - return endRes; - } - - @Override - public void setEndRes(int endRes) - { - this.endRes = endRes; + /* + * if a map is present, unless it is 1:1 and has no SequenceI mate, it cannot be a primary reference. + */ + if (map != null) + { + if (map.getTo() != null) + { + return false; + } + if (map.getMap().getFromRatio() != map.getMap().getToRatio() + || map.getMap().getFromRatio() != 1) + { + return false; + } + // check map is between identical single contiguous ranges + List fromRanges = map.getMap().getFromRanges(); + List toRanges = map.getMap().getToRanges(); + if (fromRanges.size() != 1 || toRanges.size() != 1) + { + return false; + } + if (fromRanges.get(0)[0] != toRanges.get(0)[0] + || fromRanges.get(0)[1] != toRanges.get(0)[1]) + { + return false; + } + } + if (version == null) + { + // no version string implies the reference has not been verified at all. + return false; + } + // tricky - this test really needs to search the sequence's set of dbrefs to + // see if there is a primary reference that derived this reference. + String ucv = version.toUpperCase(); + for (String primsrc : Arrays.asList(DBRefSource.allSources())) + { + if (ucv.startsWith(primsrc.toUpperCase())) + { + // by convention, many secondary references inherit the primary + // reference's + // source string as a prefix for any version information from the + // secondary reference. + return false; + } + } + return true; } } diff --git a/src/jalview/datamodel/DBRefSource.java b/src/jalview/datamodel/DBRefSource.java index 91b49eb..0ac14e5 100755 --- a/src/jalview/datamodel/DBRefSource.java +++ b/src/jalview/datamodel/DBRefSource.java @@ -20,10 +20,17 @@ */ package jalview.datamodel; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + /** * Defines internal constants for unambiguous annotation of DbRefEntry source * strings and describing the data retrieved from external database sources (see - * jalview.ws.DbSourcProxy) + * jalview.ws.DbSourcProxy)
        + * TODO: replace with ontology to allow recognition of particular attributes + * (e.g. protein coding, alignment (ortholog db, paralog db, domain db), + * genomic, transcriptomic, 3D structure providing (PDB, MODBASE, etc) ..). * * @author JimP * @@ -33,12 +40,12 @@ public class DBRefSource /** * UNIPROT Accession Number */ - public static String UNIPROT = "UNIPROT"; + public static final String UNIPROT = "UNIPROT"; /** * UNIPROT Entry Name */ - public static String UP_NAME = "UNIPROT_NAME".toUpperCase(); + public static final String UP_NAME = "UNIPROT_NAME".toUpperCase(); /** * Uniprot Knowledgebase/TrEMBL as served from EMBL protein products. @@ -51,32 +58,27 @@ public class DBRefSource /** * PDB Entry Code */ - public static String PDB = "PDB"; - - /** - * mmCIF Entry Code - */ - public static String MMCIF = "mmCIF"; + public static final String PDB = "PDB"; /** * EMBL ID */ - public static String EMBL = "EMBL"; + public static final String EMBL = "EMBL"; /** * EMBLCDS ID */ - public static String EMBLCDS = "EMBLCDS"; + public static final String EMBLCDS = "EMBLCDS"; /** * PFAM ID */ - public static String PFAM = "PFAM"; + public static final String PFAM = "PFAM"; /** * RFAM ID */ - public static String RFAM = "RFAM"; + public static final String RFAM = "RFAM"; /** * GeneDB ID @@ -88,6 +90,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 */ @@ -96,6 +100,25 @@ public class DBRefSource public static final String[] CODINGDBS = { EMBLCDS, GENEDB, ENSEMBL }; - public static final String[] PROTEINDBS = { UNIPROT, PDB, UNIPROTKB, + public static final String[] PROTEINDBS = { UNIPROT, UNIPROTKB, EMBLCDSProduct, ENSEMBL }; // Ensembl ENSP* entries are protein + + public static String[] allSources() + { + List src = new ArrayList(); + for (Field f : DBRefSource.class.getFields()) + { + if (String.class.equals(f.getType())) + { + try + { + src.add((String) f.get(null)); + } catch (Exception x) + { + x.printStackTrace(); + } + } + } + return src.toArray(new String[0]); + } } diff --git a/src/jalview/datamodel/FeatureProperties.java b/src/jalview/datamodel/FeatureProperties.java index d25eb96..2306bec 100644 --- a/src/jalview/datamodel/FeatureProperties.java +++ b/src/jalview/datamodel/FeatureProperties.java @@ -28,8 +28,7 @@ package jalview.datamodel; */ public class FeatureProperties { - - private static final String EMBL_CODING_FEATURE = "CDS"; + public static final String EMBL_CODING_FEATURE = "CDS"; public static final String EXONPOS = "exon number"; diff --git a/src/jalview/datamodel/HiddenSequences.java b/src/jalview/datamodel/HiddenSequences.java index dcc5f26..8ca3c5a 100755 --- a/src/jalview/datamodel/HiddenSequences.java +++ b/src/jalview/datamodel/HiddenSequences.java @@ -33,11 +33,21 @@ public class HiddenSequences AlignmentI alignment; + /** + * Constructor given a reference to an alignment (with no hidden sequences) + * + * @param al + */ public HiddenSequences(AlignmentI al) { alignment = al; } + /** + * Answers the number of hidden sequences + * + * @return + */ public int getSize() { if (hiddenSequences == null) @@ -45,9 +55,9 @@ public class HiddenSequences return 0; } int count = 0; - for (int i = 0; i < hiddenSequences.length; i++) + for (SequenceI seq : hiddenSequences) { - if (hiddenSequences[i] != null) + if (seq != null) { count++; } @@ -56,15 +66,23 @@ public class HiddenSequences return count; } + /** + * Answers the length of the longest hidden sequence + * + * @return + */ public int getWidth() { + if (hiddenSequences == null) + { + return 0; + } int width = 0; - for (int i = 0; i < hiddenSequences.length; i++) + for (SequenceI seq : hiddenSequences) { - if (hiddenSequences[i] != null - && hiddenSequences[i].getLength() > width) + if (seq != null && seq.getLength() > width) { - width = hiddenSequences[i].getLength(); + width = seq.getLength(); } } @@ -72,7 +90,7 @@ public class HiddenSequences } /** - * Call this method if sequences are removed from the main alignment + * Call this method after a sequence is removed from the main alignment */ public void adjustHeightSequenceDeleted(int seqIndex) { @@ -108,8 +126,7 @@ public class HiddenSequences } /** - * Call this method if sequences are added to or removed from the main - * alignment + * Call this method after a sequence is added to the main alignment */ public void adjustHeightSequenceAdded() { @@ -125,6 +142,11 @@ public class HiddenSequences hiddenSequences = tmp; } + /** + * Mark the specified sequence as hidden + * + * @param sequence + */ public void hideSequence(SequenceI sequence) { if (hiddenSequences == null) @@ -163,6 +185,17 @@ public class HiddenSequences return revealedSeqs; } + /** + * Reveals (unhides) consecutive hidden sequences just above the given + * alignment index. The revealed sequences are selected (including their + * visible representative sequence if there was one and 'reveal' is being + * performed on it). + * + * @param alignmentIndex + * @param hiddenRepSequences + * a map of representative sequences to the sequences they represent + * @return + */ public List showSequence(int alignmentIndex, Map hiddenRepSequences) { @@ -203,20 +236,22 @@ public class HiddenSequences + " has been deleted whilst hidden"); } } - } } - return revealedSeqs; } public SequenceI getHiddenSequence(int alignmentIndex) { - return hiddenSequences[alignmentIndex]; + return hiddenSequences == null ? null : hiddenSequences[alignmentIndex]; } public int findIndexWithoutHiddenSeqs(int alignmentIndex) { + if (hiddenSequences == null) + { + return alignmentIndex; + } int index = 0; int hiddenSeqs = 0; if (hiddenSequences.length <= alignmentIndex) @@ -232,13 +267,16 @@ public class HiddenSequences } index++; } - ; return (alignmentIndex - hiddenSeqs); } public int adjustForHiddenSeqs(int alignmentIndex) { + if (hiddenSequences == null) + { + return alignmentIndex; + } int index = 0; int hSize = hiddenSequences.length; while (index <= alignmentIndex && index < hSize) @@ -254,22 +292,36 @@ public class HiddenSequences return alignmentIndex; } + /** + * makes a copy of the alignment with hidden sequences included. Using the + * copy for anything other than simple output is not recommended. Note - this + * method DOES NOT USE THE AlignmentI COPY CONSTRUCTOR! + * @return + */ public AlignmentI getFullAlignment() { - int isize = hiddenSequences.length; - SequenceI[] seq = new Sequence[isize]; - - int index = 0; - for (int i = 0; i < hiddenSequences.length; i++) + SequenceI[] seq; + if (hiddenSequences == null) { - if (hiddenSequences[i] != null) - { - seq[i] = hiddenSequences[i]; - } - else + seq = alignment.getSequencesArray(); + } + else + { + int isize = hiddenSequences.length; + seq = new Sequence[isize]; + + int index = 0; + for (int i = 0; i < hiddenSequences.length; i++) { - seq[i] = alignment.getSequenceAt(index); - index++; + if (hiddenSequences[i] != null) + { + seq[i] = hiddenSequences[i]; + } + else + { + seq[i] = alignment.getSequenceAt(index); + index++; + } } } Alignment fAlignmt = new Alignment(seq); @@ -277,6 +329,7 @@ public class HiddenSequences fAlignmt.alignmentProperties = alignment.getProperties(); fAlignmt.groups = alignment.getGroups(); fAlignmt.hasRNAStructure = alignment.hasRNAStructure(); + fAlignmt.setSeqrep(alignment.getSeqrep()); return fAlignmt; } diff --git a/src/jalview/datamodel/Mapping.java b/src/jalview/datamodel/Mapping.java index bd83fe9..1c196be 100644 --- a/src/jalview/datamodel/Mapping.java +++ b/src/jalview/datamodel/Mapping.java @@ -20,6 +20,7 @@ */ package jalview.datamodel; +import jalview.util.Comparison; import jalview.util.MapList; import java.util.Iterator; @@ -258,7 +259,8 @@ public class Mapping int truePos = sequencePos - (start - 1); while (alignedBases < truePos && alignedColumn < alignedSeq.length) { - if (alignedSeq[alignedColumn++] != gap) + char c = alignedSeq[alignedColumn++]; + if (c != gap && !Comparison.isGap(c)) { alignedBases++; } @@ -274,18 +276,23 @@ public class Mapping } - /** + /* * Contains the start-end pairs mapping from the associated sequence to the * sequence in the database coordinate system. It also takes care of step * difference between coordinate systems. */ MapList map = null; - /** + /* * The sequence that map maps the associated sequence to (if any). */ SequenceI to = null; + /* + * optional sequence id for the 'from' ranges + */ + private String mappedFromId; + public Mapping(MapList map) { super(); @@ -333,6 +340,7 @@ public class Mapping map = new MapList(map2.map); } to = map2.to; + mappedFromId = map2.mappedFromId; } } @@ -356,14 +364,13 @@ public class Mapping /** * Equals that compares both the to references and MapList mappings. * - * @param other + * @param o * @return + * @see MapList#equals */ @Override public boolean equals(Object o) { - // TODO should override Object.hashCode() to ensure that equal objects have - // equal hashcodes if (o == null || !(o instanceof Mapping)) { return false; @@ -390,6 +397,21 @@ public class Mapping } /** + * Returns a hashCode made from the sequence and maplist + */ + @Override + public int hashCode() + { + int hashCode = (this.to == null ? 1 : this.to.hashCode()); + if (this.map != null) + { + hashCode = hashCode * 31 + this.map.hashCode(); + } + + return hashCode; + } + + /** * get the 'initial' position in the associated sequence for a position in the * mapped reference frame * @@ -728,4 +750,22 @@ public class Mapping : this.to.getName()); } + /** + * Returns the identifier for the 'from' range sequence, or null if not set + * + * @return + */ + public String getMappedFromId() + { + return mappedFromId; + } + + /** + * Sets the identifier for the 'from' range sequence + */ + public void setMappedFromId(String mappedFromId) + { + this.mappedFromId = mappedFromId; + } + } diff --git a/src/jalview/datamodel/PDBEntry.java b/src/jalview/datamodel/PDBEntry.java index 1c7df49..1403595 100755 --- a/src/jalview/datamodel/PDBEntry.java +++ b/src/jalview/datamodel/PDBEntry.java @@ -20,29 +20,63 @@ */ package jalview.datamodel; +import jalview.util.CaseInsensitiveString; + import java.util.Hashtable; public class PDBEntry { + private static final int PDB_ID_LENGTH = 4; + private String file; private String type; private String id; - private String chainCode; - public enum Type { - PDB, MMCIF, FILE + PDB, MMCIF, FILE; + /** + * case insensitive matching for Type enum + * + * @param value + * @return + */ + public static Type getType(String value) + { + for (Type t : Type.values()) + { + if (t.toString().equalsIgnoreCase(value)) + { + return t; + } + } + return null; + } + + /** + * case insensitive equivalence for strings resolving to PDBEntry type + * + * @param t + * @return + */ + public boolean matches(String t) + { + return (this.toString().equalsIgnoreCase(t)); + } } + /** + * constant for storing chain code in properties table + */ + private static final String CHAIN_ID = "chain_code"; + Hashtable properties; - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) + /** + * Answers true if obj is a PDBEntry with the same id and chain code (both + * ignoring case), file, type and properties */ @Override public boolean equals(Object obj) @@ -56,17 +90,24 @@ public class PDBEntry return true; } PDBEntry o = (PDBEntry) obj; - return (type == o.type || (type != null && o.type != null && o.type - .equals(type))) - && (id == o.id || (id != null && o.id != null && o.id - .equalsIgnoreCase(id))) - && (chainCode == o.chainCode || (chainCode != null - && o.chainCode != null && o.chainCode - .equalsIgnoreCase(chainCode))) - && (properties == o.properties || (properties != null - && o.properties != null && properties - .equals(o.properties))); + /* + * note that chain code is stored as a property wrapped by a + * CaseInsensitiveString, so we are in effect doing a + * case-insensitive comparison of chain codes + */ + boolean idMatches = id == o.id + || (id != null && id.equalsIgnoreCase(o.id)); + boolean fileMatches = file == o.file + || (file != null && file.equals(o.file)); + boolean typeMatches = type == o.type + || (type != null && type.equals(o.type)); + if (idMatches && fileMatches && typeMatches) + { + return properties == o.properties + || (properties != null && properties.equals(o.properties)); + } + return false; } /** @@ -90,10 +131,21 @@ public class PDBEntry public PDBEntry(String pdbId, String chain, PDBEntry.Type type, String filePath) { + init(pdbId, chain, type, filePath); + } + + /** + * @param pdbId + * @param chain + * @param type + * @param filePath + */ + void init(String pdbId, String chain, PDBEntry.Type type, String filePath) + { this.id = pdbId; - this.chainCode = chain; this.type = type == null ? null : type.toString(); this.file = filePath; + setChainCode(chain); } /** @@ -106,13 +158,41 @@ public class PDBEntry file = entry.file; type = entry.type; id = entry.id; - chainCode = entry.chainCode; if (entry.properties != null) { properties = (Hashtable) entry.properties.clone(); } } + /** + * Make a PDBEntry from a DBRefEntry. The accession code is used for the PDB + * id, but if it is 5 characters in length, the last character is removed and + * set as the chain code instead. + * + * @param dbr + */ + public PDBEntry(DBRefEntry dbr) + { + if (!DBRefSource.PDB.equals(dbr.getSource())) + { + throw new IllegalArgumentException("Invalid source: " + + dbr.getSource()); + } + + String pdbId = dbr.getAccessionId(); + String chainCode = null; + if (pdbId.length() == PDB_ID_LENGTH + 1) + { + char chain = pdbId.charAt(PDB_ID_LENGTH); + if (('a' <= chain && chain <= 'z') || ('A' <= chain && chain <= 'Z')) + { + pdbId = pdbId.substring(0, PDB_ID_LENGTH); + chainCode = String.valueOf(chain); + } + } + init(pdbId, chainCode, null, null); + } + public void setFile(String file) { this.file = file; @@ -158,14 +238,34 @@ public class PDBEntry return properties; } + /** + * + * @return null or a string for associated chain IDs + */ public String getChainCode() { - return chainCode; + return (properties == null || properties.get(CHAIN_ID) == null) ? null + : properties.get(CHAIN_ID).toString(); } public void setChainCode(String chainCode) { - this.chainCode = chainCode; + if (properties == null) + { + if (chainCode == null) + { + // nothing to do. + return; + } + properties = new Hashtable(); + } + if (chainCode == null) + { + properties.remove(CHAIN_ID); + return; + } + // update property for non-null chainCode + properties.put(CHAIN_ID, new CaseInsensitiveString(chainCode)); } @Override @@ -173,4 +273,111 @@ public class PDBEntry { return id; } + + /** + * Answers true if this object is either equivalent to, or can be 'improved' + * by, the given entry. + *

        + * If newEntry has the same id (ignoring case), and doesn't have a conflicting + * file spec or chain code, then update this entry from its file and/or chain + * code. + * + * @param newEntry + * @return true if modifications were made + */ + protected boolean updateFrom(PDBEntry newEntry) + { + if (this.equals(newEntry)) + { + return true; + } + + String newId = newEntry.getId(); + if (newId == null || getId() == null) + { + return false; // shouldn't happen + } + + /* + * id (less any chain code) has to match (ignoring case) + */ + if (!getId().equalsIgnoreCase(newId)) + { + return false; + } + + /* + * Don't update if associated with different structure files + */ + String newFile = newEntry.getFile(); + if (newFile != null && getFile() != null && !newFile.equals(getFile())) + { + return false; + } + + /* + * Don't update if associated with different chains (ignoring case) + */ + String newChain = newEntry.getChainCode(); + if (newChain != null && newChain.length() > 0 && getChainCode() != null + && getChainCode().length() > 0 + && !getChainCode().equalsIgnoreCase(newChain)) + { + return false; + } + + /* + * set file path if not already set + */ + String newType = newEntry.getType(); + if (getFile() == null && newFile != null) + { + setFile(newFile); + setType(newType); + } + + /* + * set file type if new entry has it and we don't + * (for the case where file was not updated) + */ + if (getType() == null && newType != null) + { + setType(newType); + } + + /* + * set chain if not already set (we excluded differing + * chains earlier) (ignoring case change only) + */ + if (newChain != null && newChain.length() > 0 + && !newChain.equalsIgnoreCase(getChainCode())) + { + setChainCode(newChain); + } + + /* + * copy any new properties; notice this may include chain_code, + * but we excluded differing chain codes earlier + */ + if (newEntry.getProperty() != null) + { + if (properties == null) + { + properties = new Hashtable(); + } + for (Object p : newEntry.getProperty().keySet()) + { + /* + * copy properties unless value matches; this defends against changing + * the case of chain_code which is wrapped in a CaseInsensitiveString + */ + Object value = newEntry.getProperty().get(p); + if (!value.equals(properties.get(p))) + { + properties.put(p, newEntry.getProperty().get(p)); + } + } + } + return true; + } } diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index a61f093..44522a8 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -22,10 +22,13 @@ package jalview.datamodel; import jalview.analysis.AlignSeq; import jalview.api.DBRefEntryI; +import jalview.util.DBRefUtils; +import jalview.util.MapList; import jalview.util.StringUtils; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Vector; @@ -57,8 +60,6 @@ public class Sequence extends ASequence implements SequenceI String vamsasId; - DBRefEntryI sourceDBRef; - DBRefEntry[] dbrefs; RNA rna; @@ -235,8 +236,6 @@ public class Sequence extends ASequence implements SequenceI seq.getEnd()); } description = seq.getDescription(); - sourceDBRef = seq.getSourceDBRef() == null ? null : new DBRefEntry( - seq.getSourceDBRef()); if (seq != datasetSequence) { setDatasetSequence(seq.getDatasetSequence()); @@ -303,8 +302,14 @@ public class Sequence extends ASequence implements SequenceI } else { - System.err - .println("Warning: JAL-2046 side effect ? Possible implementation error: overwriting dataset sequence features by setting sequence features on alignment"); + if (datasetSequence.getSequenceFeatures() != features + && datasetSequence.getSequenceFeatures() != null + && datasetSequence.getSequenceFeatures().length > 0) + { + new Exception( + "Warning: JAL-2046 side effect ? Possible implementation error: overwriting dataset sequence features by setting sequence features on alignment") + .printStackTrace(); + } datasetSequence.setSequenceFeatures(features); } } @@ -407,28 +412,24 @@ public class Sequence extends ASequence implements SequenceI } @Override - public void addPDBId(PDBEntry entry) + public boolean addPDBId(PDBEntry entry) { if (pdbIds == null) { pdbIds = new Vector(); + pdbIds.add(entry); + return true; } - if (pdbIds.contains(entry)) - { - updatePDBEntry(pdbIds.get(pdbIds.indexOf(entry)), entry); - } - else - { - pdbIds.addElement(entry); - } - } - private static void updatePDBEntry(PDBEntry oldEntry, PDBEntry newEntry) - { - if (newEntry.getFile() != null) + for (PDBEntry pdbe : pdbIds) { - oldEntry.setFile(newEntry.getFile()); + if (pdbe.updateFrom(entry)) + { + return false; + } } + pdbIds.addElement(entry); + return true; } /** @@ -966,26 +967,25 @@ public class Sequence extends ASequence implements SequenceI dbrefs = new DBRefEntry[0]; } - int i, iSize = dbrefs.length; - - for (i = 0; i < iSize; i++) + for (DBRefEntryI dbr : dbrefs) { - if (dbrefs[i].equalRef(entry)) + if (dbr.updateFrom(entry)) { - if (entry.getMap() != null) - { - if (dbrefs[i].getMap() == null) - { - // overwrite with 'superior' entry that contains a mapping. - dbrefs[i] = entry; - } - } + /* + * found a dbref that either matched, or could be + * updated from, the new entry - no need to add it + */ return; } } - DBRefEntry[] temp = new DBRefEntry[iSize + 1]; - System.arraycopy(dbrefs, 0, temp, 0, iSize); + /* + * extend the array to make room for one more + */ + // TODO use an ArrayList instead + int j = dbrefs.length; + DBRefEntry[] temp = new DBRefEntry[j + 1]; + System.arraycopy(dbrefs, 0, temp, 0, j); temp[temp.length - 1] = entry; dbrefs = temp; @@ -1087,6 +1087,25 @@ public class Sequence extends ASequence implements SequenceI return new Sequence(this); } + private boolean _isNa; + + private long _seqhash = 0; + + @Override + public boolean isProtein() + { + if (datasetSequence != null) + { + return datasetSequence.isProtein(); + } + if (_seqhash != sequence.hashCode()) + { + _seqhash = sequence.hashCode(); + _isNa=jalview.util.Comparison.isNucleotide(new SequenceI[] { this }); + } + return !_isNa; + }; + /* * (non-Javadoc) * @@ -1202,46 +1221,22 @@ public class Sequence extends ASequence implements SequenceI { return false; } - Vector newpdb = new Vector(); - for (int i = 0; i < dbrefs.length; i++) - { - if (DBRefSource.PDB.equals(dbrefs[i].getSource())) - { - PDBEntry pdbe = new PDBEntry(); - pdbe.setId(dbrefs[i].getAccessionId()); - if (pdbIds == null || pdbIds.size() == 0) - { - newpdb.addElement(pdbe); - } - else - { - Enumeration en = pdbIds.elements(); - boolean matched = false; - while (!matched && en.hasMoreElements()) - { - PDBEntry anentry = (PDBEntry) en.nextElement(); - if (anentry.getId().equals(pdbe.getId())) - { - matched = true; - } - } - if (!matched) - { - newpdb.addElement(pdbe); - } - } - } - } - if (newpdb.size() > 0) + boolean added = false; + for (DBRefEntry dbr : dbrefs) { - Enumeration en = newpdb.elements(); - while (en.hasMoreElements()) + if (DBRefSource.PDB.equals(dbr.getSource())) { - addPDBId((PDBEntry) en.nextElement()); + /* + * 'Add' any PDB dbrefs as a PDBEntry - add is only performed if the + * PDB id is not already present in a 'matching' PDBEntry + * Constructor parses out a chain code if appended to the accession id + * (a fudge used to 'store' the chain code in the DBRef) + */ + PDBEntry pdbe = new PDBEntry(dbr); + added |= addPDBId(pdbe); } - return true; } - return false; + return added; } @Override @@ -1371,12 +1366,15 @@ public class Sequence extends ASequence implements SequenceI @Override public PDBEntry getPDBEntry(String pdbIdStr) { - if (getDatasetSequence() == null - || getDatasetSequence().getAllPDBEntries() == null) + if (getDatasetSequence() != null) + { + return getDatasetSequence().getPDBEntry(pdbIdStr); + } + if (pdbIds == null) { return null; } - List entries = getDatasetSequence().getAllPDBEntries(); + List entries = getAllPDBEntries(); for (PDBEntry entry : entries) { if (entry.getId().equalsIgnoreCase(pdbIdStr)) @@ -1387,16 +1385,65 @@ public class Sequence extends ASequence implements SequenceI return null; } - @Override - public void setSourceDBRef(DBRefEntryI dbRef) - { - this.sourceDBRef = dbRef; - } @Override - public DBRefEntryI getSourceDBRef() + public List getPrimaryDBRefs() { - return this.sourceDBRef; + if (datasetSequence!=null) + { + return datasetSequence.getPrimaryDBRefs(); + } + if (dbrefs==null || dbrefs.length==0) + { + return Collections.emptyList(); + } + synchronized (dbrefs) + { + List primaries = new ArrayList(); + DBRefEntry[] tmp = new DBRefEntry[1]; + for (DBRefEntry ref : dbrefs) + { + if (!ref.isPrimaryCandidate()) + { + continue; + } + if (ref.hasMap()) + { + MapList mp = ref.getMap().getMap(); + if (mp.getFromLowest() > start || mp.getFromHighest() < end) + { + // map only involves a subsequence, so cannot be primary + continue; + } + } + // whilst it looks like it is a primary ref, we also sanity check type + if (DBRefUtils.getCanonicalName(DBRefSource.PDB).equals( + DBRefUtils.getCanonicalName(ref.getSource()))) + { + // PDB dbrefs imply there should be a PDBEntry associated + // TODO: tighten PDB dbrefs + // formally imply Jalview has actually downloaded and + // parsed the pdb file. That means there should be a cached file + // handle on the PDBEntry, and a real mapping between sequence and + // extracted sequence from PDB file + PDBEntry pdbentry = getPDBEntry(ref.getAccessionId()); + if (pdbentry != null && pdbentry.getFile() != null) + { + primaries.add(ref); + } + continue; + } + // check standard protein or dna sources + tmp[0] = ref; + DBRefEntry[] res = DBRefUtils.selectDbRefs(!isProtein(), tmp); + if (res != null && res[0] == tmp[0]) + { + primaries.add(ref); + continue; + } + } + return primaries; + } } } diff --git a/src/jalview/datamodel/SequenceFeature.java b/src/jalview/datamodel/SequenceFeature.java index f2eb8ac..c75d6f2 100755 --- a/src/jalview/datamodel/SequenceFeature.java +++ b/src/jalview/datamodel/SequenceFeature.java @@ -39,6 +39,9 @@ public class SequenceFeature // private key for Phase designed not to conflict with real GFF data private static final String PHASE = "!Phase"; + // private key for ENA location designed not to conflict with real GFF data + private static final String LOCATION = "!Location"; + /* * ATTRIBUTES is reserved for the GFF 'column 9' data, formatted as * name1=value1;name2=value2,value3;...etc @@ -55,6 +58,10 @@ public class SequenceFeature public String description; + /* + * a map of key-value pairs; may be populated from GFF 'column 9' data, + * other data sources (e.g. GenBank file), or programmatically + */ public Map otherDetails; public Vector links; @@ -480,6 +487,26 @@ public class SequenceFeature } /** + * Sets the 'raw' ENA format location specifier e.g. join(12..45,89..121) + * + * @param loc + */ + public void setEnaLocation(String loc) + { + setValue(LOCATION, loc); + } + + /** + * Gets the 'raw' ENA format location specifier e.g. join(12..45,89..121) + * + * @param loc + */ + public String getEnaLocation() + { + return (String) getValue(LOCATION); + } + + /** * Readable representation, for debug only, not guaranteed not to change * between versions */ diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java index b9cdf0e..3199240 100755 --- a/src/jalview/datamodel/SequenceGroup.java +++ b/src/jalview/datamodel/SequenceGroup.java @@ -506,14 +506,32 @@ public class SequenceGroup implements AnnotatedCollectionI } /** - * calculate residue conservation for group - but only if necessary. + * calculate residue conservation and colourschemes for group - but only if + * necessary. returns true if the calculation resulted in a visible change to + * group */ - public void recalcConservation() + public boolean recalcConservation() + { + return recalcConservation(false); + } + + /** + * calculate residue conservation for group - but only if necessary. returns + * true if the calculation resulted in a visible change to group + * + * @param defer + * when set, colourschemes for this group are not refreshed after + * recalculation + */ + public boolean recalcConservation(boolean defer) { if (cs == null && consensus == null && conservation == null) { - return; + return false; } + // TODO: try harder to detect changes in state in order to minimise + // recalculation effort + boolean upd = false; try { Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes, @@ -521,10 +539,12 @@ public class SequenceGroup implements AnnotatedCollectionI if (consensus != null) { _updateConsensusRow(cnsns, sequences.size()); + upd = true; } if (cs != null) { cs.setConsensus(cnsns); + upd = true; } if ((conservation != null) @@ -546,17 +566,25 @@ public class SequenceGroup implements AnnotatedCollectionI cs.setConservation(c); } } + // eager update - will cause a refresh of overview regardless + upd = true; } - if (cs != null) + if (cs != null && !defer) { + // TODO: JAL-2034 should cs.alignmentChanged modify return state cs.alignmentChanged(context != null ? context : this, null); + return true; + } + else + { + return upd; } } catch (java.lang.OutOfMemoryError err) { // TODO: catch OOM System.out.println("Out of memory loading groups: " + err); } - + return upd; } private void _updateConservationRow(Conservation c) @@ -1041,7 +1069,8 @@ public class SequenceGroup implements AnnotatedCollectionI /** * - * @return automatically calculated consensus row + * @return automatically calculated consensus row note: the row is a stub if a + * consensus calculation has not yet been performed on the group */ public AlignmentAnnotation getConsensus() { diff --git a/src/jalview/datamodel/SequenceI.java b/src/jalview/datamodel/SequenceI.java index 60040d8..b7a291e 100755 --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@ -20,8 +20,6 @@ */ package jalview.datamodel; -import jalview.api.DBRefEntryI; - import java.util.List; import java.util.Vector; @@ -219,6 +217,12 @@ public interface SequenceI extends ASequenceI public int[] findPositionMap(); /** + * + * @return true if sequence is composed of amino acid characters + */ + public boolean isProtein(); + + /** * Delete a range of aligned sequence columns, creating a new dataset sequence * if necessary and adjusting start and end positions accordingly. * @@ -233,19 +237,21 @@ public interface SequenceI extends ASequenceI * DOCUMENT ME! * * @param i - * DOCUMENT ME! + * alignment column number * @param c - * DOCUMENT ME! + * character to insert */ public void insertCharAt(int i, char c); /** - * DOCUMENT ME! + * insert given character at alignment column position * * @param position - * DOCUMENT ME! + * alignment column number + * @param count + * length of insert * @param ch - * DOCUMENT ME! + * character to insert */ public void insertCharAt(int position, int count, char ch); @@ -283,11 +289,18 @@ public interface SequenceI extends ASequenceI public Vector getAllPDBEntries(); /** - * add entry to the vector of PDBIds, if it isn't in the list already + * Adds the entry to the *normalised* list of PDBIds. + * + * If a PDBEntry is passed with the same entry.getID() string as one already + * in the list, or one is added that appears to be the same but has a chain ID + * appended, then the existing PDBEntry will be updated with the new + * attributes instead, unless the entries have distinct chain codes or + * associated structure files. * * @param entry + * @return true if the entry was added, false if updated */ - public void addPDBId(PDBEntry entry); + public boolean addPDBId(PDBEntry entry); /** * update the list of PDBEntrys to include any DBRefEntrys citing structural @@ -435,7 +448,14 @@ public interface SequenceI extends ASequenceI */ public PDBEntry getPDBEntry(String pdbId); - public void setSourceDBRef(DBRefEntryI dbRef); - public DBRefEntryI getSourceDBRef(); + /** + * Get all primary database/accessions for this sequence's data. These + * DBRefEntry are expected to resolve to a valid record in the associated + * external database, either directly or via a provided 1:1 Mapping. + * + * @return just the primary references (if any) for this sequence, or an empty + * list + */ + public List getPrimaryDBRefs(); } diff --git a/src/jalview/datamodel/xdb/embl/BasePosition.java b/src/jalview/datamodel/xdb/embl/BasePosition.java deleted file mode 100644 index 3737adc..0000000 --- a/src/jalview/datamodel/xdb/embl/BasePosition.java +++ /dev/null @@ -1,68 +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 . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ -package jalview.datamodel.xdb.embl; - -/** - * Data model for a feature/location/locationElement/basePosition read from an - * EMBL query reply - * - * @see embl_mapping.xml - */ -public class BasePosition -{ - String type; - - String pos; - - /** - * @return the pos - */ - public String getPos() - { - return pos; - } - - /** - * @param pos - * the pos to set - */ - public void setPos(String pos) - { - this.pos = pos; - } - - /** - * @return the type - */ - public String getType() - { - return type; - } - - /** - * @param type - * the type to set - */ - public void setType(String type) - { - this.type = type; - } -} diff --git a/src/jalview/datamodel/xdb/embl/EmblEntry.java b/src/jalview/datamodel/xdb/embl/EmblEntry.java index 691a4c9..3ba36ca 100644 --- a/src/jalview/datamodel/xdb/embl/EmblEntry.java +++ b/src/jalview/datamodel/xdb/embl/EmblEntry.java @@ -21,6 +21,7 @@ package jalview.datamodel.xdb.embl; import jalview.analysis.SequenceIdMatcher; +import jalview.bin.Cache; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; import jalview.datamodel.FeatureProperties; @@ -29,10 +30,12 @@ import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.util.DBRefUtils; +import jalview.util.DnaUtils; import jalview.util.MapList; import jalview.util.MappingUtils; import jalview.util.StringUtils; +import java.text.ParseException; import java.util.Arrays; import java.util.Hashtable; import java.util.List; @@ -46,8 +49,7 @@ import java.util.regex.Pattern; * Castor binding file * * For example: - * http://www.ebi.ac.uk/Tools/dbfetch/dbfetch?db=ena_sequence&id=J03321 - * &format=emblxml + * http://www.ebi.ac.uk/ena/data/view/J03321&display=xml * * @see embl_mapping.xml */ @@ -57,17 +59,29 @@ public class EmblEntry String accession; - String version; + String entryVersion; - String taxDivision; + String sequenceVersion; - String desc; + String dataClass; - String rCreated; + String moleculeType; - String rLastUpdated; + String topology; - String lastUpdated; + String sequenceLength; + + String taxonomicDivision; + + String description; + + String firstPublicDate; + + String firstPublicRelease; + + String lastUpdatedDate; + + String lastUpdatedRelease; Vector keywords; @@ -112,23 +126,6 @@ public class EmblEntry } /** - * @return the desc - */ - public String getDesc() - { - return desc; - } - - /** - * @param desc - * the desc to set - */ - public void setDesc(String desc) - { - this.desc = desc; - } - - /** * @return the features */ public Vector getFeatures() @@ -163,57 +160,6 @@ public class EmblEntry } /** - * @return the lastUpdated - */ - public String getLastUpdated() - { - return lastUpdated; - } - - /** - * @param lastUpdated - * the lastUpdated to set - */ - public void setLastUpdated(String lastUpdated) - { - this.lastUpdated = lastUpdated; - } - - /** - * @return the releaseCreated - */ - public String getRCreated() - { - return rCreated; - } - - /** - * @param releaseCreated - * the releaseCreated to set - */ - public void setRCreated(String releaseCreated) - { - this.rCreated = releaseCreated; - } - - /** - * @return the releaseLastUpdated - */ - public String getRLastUpdated() - { - return rLastUpdated; - } - - /** - * @param releaseLastUpdated - * the releaseLastUpdated to set - */ - public void setRLastUpdated(String releaseLastUpdated) - { - this.rLastUpdated = releaseLastUpdated; - } - - /** * @return the sequence */ public EmblSequence getSequence() @@ -231,40 +177,6 @@ public class EmblEntry } /** - * @return the taxDivision - */ - public String getTaxDivision() - { - return taxDivision; - } - - /** - * @param taxDivision - * the taxDivision to set - */ - public void setTaxDivision(String taxDivision) - { - this.taxDivision = taxDivision; - } - - /** - * @return the version - */ - public String getVersion() - { - return version; - } - - /** - * @param version - * the version to set - */ - public void setVersion(String version) - { - this.version = version; - } - - /** * Recover annotated sequences from EMBL file * * @param sourceDb @@ -274,38 +186,41 @@ public class EmblEntry */ public SequenceI getSequence(String sourceDb, List peptides) { - SequenceI dna = new Sequence(sourceDb + "|" + accession, - sequence.getSequence()); - dna.setDescription(desc); - DBRefEntry retrievedref = new DBRefEntry(sourceDb, version, accession); + SequenceI dna = makeSequence(sourceDb); + if (dna == null) + { + return null; + } + dna.setDescription(description); + DBRefEntry retrievedref = new DBRefEntry(sourceDb, + getSequenceVersion(), accession); dna.addDBRef(retrievedref); // add map to indicate the sequence is a valid coordinate frame for the // dbref retrievedref.setMap(new Mapping(null, new int[] { 1, dna.getLength() }, new int[] { 1, dna.getLength() }, 1, 1)); - // TODO: transform EMBL Database refs to canonical form + + + /* + * transform EMBL Database refs to canonical form + */ if (dbRefs != null) { for (DBRefEntry dbref : dbRefs) { + dbref.setSource(DBRefUtils.getCanonicalName(dbref.getSource())); dna.addDBRef(dbref); } } + SequenceIdMatcher matcher = new SequenceIdMatcher(peptides); try { for (EmblFeature feature : features) { - if (feature.dbRefs != null) - { - for (DBRefEntry dbref : feature.dbRefs) - { - dna.addDBRef(dbref); - } - } if (FeatureProperties.isCodingFeature(sourceDb, feature.getName())) { - parseCodingFeature(feature, sourceDb, dna, peptides); + parseCodingFeature(feature, sourceDb, dna, peptides, matcher); } } } catch (Exception e) @@ -322,6 +237,23 @@ public class EmblEntry } /** + * @param sourceDb + * @return + */ + SequenceI makeSequence(String sourceDb) + { + if (sequence == null) + { + System.err.println("No sequence was returned for ENA accession " + + accession); + return null; + } + SequenceI dna = new Sequence(sourceDb + "|" + accession, + sequence.getSequence()); + return dna; + } + + /** * Extracts coding region and product from a CDS feature and properly decorate * it with annotations. * @@ -333,19 +265,20 @@ public class EmblEntry * parent dna sequence for this record * @param peptides * list of protein product sequences for Embl entry + * @param matcher + * helper to match xrefs in already retrieved sequences */ void parseCodingFeature(EmblFeature feature, String sourceDb, - SequenceI dna, List peptides) + SequenceI dna, List peptides, SequenceIdMatcher matcher) { boolean isEmblCdna = sourceDb.equals(DBRefSource.EMBLCDS); - int[] exon = getCdsRanges(feature); + int[] exons = getCdsRanges(feature); - String prseq = null; - String prname = ""; - String prid = null; + String translation = null; + String proteinName = ""; + String proteinId = null; Map vals = new Hashtable(); - SequenceIdMatcher matcher = new SequenceIdMatcher(peptides); /* * codon_start 1/2/3 in EMBL corresponds to phase 0/1/2 in CDS @@ -365,17 +298,17 @@ public class EmblEntry if (qname.equals("translation")) { // remove all spaces (precompiled String.replaceAll(" ", "")) - prseq = SPACE_PATTERN.matcher(q.getValues()[0]).replaceAll(""); + translation = SPACE_PATTERN.matcher(q.getValues()[0]).replaceAll(""); } else if (qname.equals("protein_id")) { - prid = q.getValues()[0]; + proteinId = q.getValues()[0].trim(); } else if (qname.equals("codon_start")) { try { - codonStart = Integer.parseInt(q.getValues()[0]); + codonStart = Integer.parseInt(q.getValues()[0].trim()); } catch (NumberFormatException e) { System.err.println("Invalid codon_start in XML for " @@ -385,7 +318,7 @@ public class EmblEntry else if (qname.equals("product")) { // sometimes name is returned e.g. for V00488 - prname = q.getValues()[0]; + proteinName = q.getValues()[0].trim(); } else { @@ -401,54 +334,59 @@ public class EmblEntry } } - DBRefEntry protEMBLCDS = null; - exon = MappingUtils.removeStartPositions(codonStart - 1, exon); - boolean noProteinDbref = true; + DBRefEntry proteinToEmblProteinRef = null; + exons = MappingUtils.removeStartPositions(codonStart - 1, exons); SequenceI product = null; - Mapping map = null; - if (prseq != null && prname != null && prid != null) + Mapping dnaToProteinMapping = null; + if (translation != null && proteinName != null && proteinId != null) { + int translationLength = translation.length(); + /* * look for product in peptides list, if not found, add it */ - product = matcher.findIdMatch(prid); + product = matcher.findIdMatch(proteinId); if (product == null) { - product = new Sequence(prid, prseq, 1, prseq.length()); - product.setDescription(((prname.length() == 0) ? "Protein Product from " + product = new Sequence(proteinId, translation, 1, translationLength); + product.setDescription(((proteinName.length() == 0) ? "Protein Product from " + sourceDb - : prname)); + : proteinName)); peptides.add(product); matcher.add(product); } // we have everything - create the mapping and perhaps the protein // sequence - if (exon == null || exon.length == 0) + if (exons == null || exons.length == 0) { + /* + * workaround until we handle dna location for CDS sequence + * e.g. location="X53828.1:60..1058" correctly + */ System.err .println("Implementation Notice: EMBLCDS records not properly supported yet - Making up the CDNA region of this sequence... may be incorrect (" + sourceDb + ":" + getAccession() + ")"); - if (prseq.length() * 3 == (1 - codonStart + dna.getSequence().length)) + if (translationLength * 3 == (1 - codonStart + dna.getSequence().length)) { System.err .println("Not allowing for additional stop codon at end of cDNA fragment... !"); - // this might occur for CDS sequences where no features are - // marked. - exon = new int[] { dna.getStart() + (codonStart - 1), + // this might occur for CDS sequences where no features are marked + exons = new int[] { dna.getStart() + (codonStart - 1), dna.getEnd() }; - map = new Mapping(product, exon, new int[] { 1, prseq.length() }, - 3, 1); + dnaToProteinMapping = new Mapping(product, exons, new int[] { 1, + translationLength }, 3, 1); } - if ((prseq.length() + 1) * 3 == (1 - codonStart + dna.getSequence().length)) + if ((translationLength + 1) * 3 == (1 - codonStart + dna + .getSequence().length)) { System.err .println("Allowing for additional stop codon at end of cDNA fragment... will probably cause an error in VAMSAs!"); - exon = new int[] { dna.getStart() + (codonStart - 1), + exons = new int[] { dna.getStart() + (codonStart - 1), dna.getEnd() - 3 }; - map = new Mapping(product, exon, new int[] { 1, prseq.length() }, - 3, 1); + dnaToProteinMapping = new Mapping(product, exons, new int[] { 1, + translationLength }, 3, 1); } } else @@ -467,57 +405,77 @@ public class EmblEntry else { // final product length truncation check - // TODO should from range include stop codon even if not in protein - // in order to include stop codon in CDS sequence (as done for - // Ensembl)? - int[] cdsRanges = adjustForProteinLength(prseq.length(), exon); - map = new Mapping(product, cdsRanges, new int[] { 1, - prseq.length() }, 3, 1); - // reconstruct the EMBLCDS entry - // TODO: this is only necessary when there codon annotation is - // complete (I think JBPNote) - DBRefEntry pcdnaref = new DBRefEntry(); - pcdnaref.setAccessionId(prid); - pcdnaref.setSource(DBRefSource.EMBLCDS); - pcdnaref.setVersion(getVersion()); // same as parent EMBL version. - MapList mp = new MapList(new int[] { 1, prseq.length() }, - new int[] { 1 + (codonStart - 1), - (codonStart - 1) + 3 * prseq.length() }, 1, 3); - pcdnaref.setMap(new Mapping(mp)); + int[] cdsRanges = adjustForProteinLength(translationLength, exons); + dnaToProteinMapping = new Mapping(product, cdsRanges, new int[] { + 1, translationLength }, 3, 1); if (product != null) { - product.addDBRef(pcdnaref); - protEMBLCDS = new DBRefEntry(pcdnaref); - protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct); - product.addDBRef(protEMBLCDS); + /* + * make xref with mapping from protein to EMBL dna + */ + DBRefEntry proteinToEmblRef = new DBRefEntry(DBRefSource.EMBL, + getSequenceVersion(), proteinId, new Mapping( + dnaToProteinMapping.getMap().getInverse())); + product.addDBRef(proteinToEmblRef); + + /* + * make xref from protein to EMBLCDS; we assume here that the + * CDS sequence version is same as dna sequence (?!) + */ + MapList proteinToCdsMapList = new MapList(new int[] { 1, + translationLength }, new int[] { 1 + (codonStart - 1), + (codonStart - 1) + 3 * translationLength }, 1, 3); + DBRefEntry proteinToEmblCdsRef = new DBRefEntry( + DBRefSource.EMBLCDS, getSequenceVersion(), proteinId, + new Mapping(proteinToCdsMapList)); + product.addDBRef(proteinToEmblCdsRef); + + /* + * make 'direct' xref from protein to EMBLCDSPROTEIN + */ + proteinToEmblProteinRef = new DBRefEntry(proteinToEmblCdsRef); + proteinToEmblProteinRef.setSource(DBRefSource.EMBLCDSProduct); + proteinToEmblProteinRef.setMap(null); + product.addDBRef(proteinToEmblProteinRef); } } } - // add cds feature to dna seq - this may include the stop codon - for (int xint = 0; exon != null && xint < exon.length; xint += 2) + + /* + * add cds features to dna sequence + */ + for (int xint = 0; exons != null && xint < exons.length; xint += 2) { - SequenceFeature sf = makeCdsFeature(exon, xint, prname, prid, vals, - codonStart); + SequenceFeature sf = makeCdsFeature(exons, xint, proteinName, + proteinId, vals, codonStart); sf.setType(feature.getName()); // "CDS" + sf.setEnaLocation(feature.getLocation()); sf.setFeatureGroup(sourceDb); dna.addSequenceFeature(sf); } } /* - * add dbRefs to sequence, and mappings for Uniprot xrefs + * add feature dbRefs to sequence, and mappings for Uniprot xrefs */ + boolean hasUniprotDbref = false; if (feature.dbRefs != null) { boolean mappingUsed = false; for (DBRefEntry ref : feature.dbRefs) { - ref.setSource(DBRefUtils.getCanonicalName(ref.getSource())); - if (ref.getSource().equals(DBRefSource.UNIPROT)) + /* + * ensure UniProtKB/Swiss-Prot converted to UNIPROT + */ + String source = DBRefUtils.getCanonicalName(ref.getSource()); + ref.setSource(source); + DBRefEntry proteinDbRef = new DBRefEntry(ref.getSource(), ref.getVersion(), ref + .getAccessionId()); + if (source.equals(DBRefSource.UNIPROT)) { String proteinSeqName = DBRefSource.UNIPROT + "|" + ref.getAccessionId(); - if (map != null && map.getTo() != null) + if (dnaToProteinMapping != null && dnaToProteinMapping.getTo() != null) { if (mappingUsed) { @@ -525,13 +483,14 @@ public class EmblEntry * two or more Uniprot xrefs for the same CDS - * each needs a distinct Mapping (as to a different sequence) */ - map = new Mapping(map); + dnaToProteinMapping = new Mapping(dnaToProteinMapping); } mappingUsed = true; /* * try to locate the protein mapped to (possibly by a - * previous CDS feature) + * previous CDS feature); if not found, construct it from + * the EMBL translation */ SequenceI proteinSeq = matcher.findIdMatch(proteinSeqName); if (proteinSeq == null) @@ -541,61 +500,62 @@ public class EmblEntry matcher.add(proteinSeq); peptides.add(proteinSeq); } - map.setTo(proteinSeq); - map.getTo().addDBRef( - new DBRefEntry(ref.getSource(), ref.getVersion(), ref - .getAccessionId())); - ref.setMap(map); + dnaToProteinMapping.setTo(proteinSeq); + dnaToProteinMapping.setMappedFromId(proteinId); + proteinSeq.addDBRef(proteinDbRef); + ref.setMap(dnaToProteinMapping); } - noProteinDbref = false; + hasUniprotDbref = true; } if (product != null) { - DBRefEntry pref = new DBRefEntry(ref.getSource(), - ref.getVersion(), ref.getAccessionId()); + /* + * copy feature dbref to our protein product + */ + DBRefEntry pref = proteinDbRef; pref.setMap(null); // reference is direct product.addDBRef(pref); // Add converse mapping reference - if (map != null) + if (dnaToProteinMapping != null) { - Mapping pmap = new Mapping(dna, map.getMap().getInverse()); - pref = new DBRefEntry(sourceDb, getVersion(), + Mapping pmap = new Mapping(dna, dnaToProteinMapping.getMap() + .getInverse()); + pref = new DBRefEntry(sourceDb, getSequenceVersion(), this.getAccession()); pref.setMap(pmap); - if (map.getTo() != null) + if (dnaToProteinMapping.getTo() != null) { - map.getTo().addDBRef(pref); + dnaToProteinMapping.getTo().addDBRef(pref); } } } dna.addDBRef(ref); } - if (noProteinDbref && product != null) + } + + /* + * if we have a product (translation) but no explicit Uniprot dbref + * (example: EMBL AAFI02000057 protein_id EAL65544.1) + * then construct mappings to an assumed EMBLCDSPROTEIN accession + */ + if (!hasUniprotDbref && product != null) + { + if (proteinToEmblProteinRef == null) { - // add protein coding reference to dna sequence so xref matches - if (protEMBLCDS == null) - { - protEMBLCDS = new DBRefEntry(); - protEMBLCDS.setAccessionId(prid); - protEMBLCDS.setSource(DBRefSource.EMBLCDSProduct); - protEMBLCDS.setVersion(getVersion()); - protEMBLCDS - .setMap(new Mapping(product, map.getMap().getInverse())); - } - product.addDBRef(protEMBLCDS); + // assuming CDSPROTEIN sequence version = dna version (?!) + proteinToEmblProteinRef = new DBRefEntry( + DBRefSource.EMBLCDSProduct, getSequenceVersion(), proteinId); + } + product.addDBRef(proteinToEmblProteinRef); - // Add converse mapping reference - if (map != null) - { - Mapping pmap = new Mapping(product, protEMBLCDS.getMap().getMap() - .getInverse()); - DBRefEntry ncMap = new DBRefEntry(protEMBLCDS); - ncMap.setMap(pmap); - if (map.getTo() != null) - { - dna.addDBRef(ncMap); - } - } + if (dnaToProteinMapping != null + && dnaToProteinMapping.getTo() != null) + { + DBRefEntry dnaToEmblProteinRef = new DBRefEntry( + DBRefSource.EMBLCDSProduct, getSequenceVersion(), proteinId); + dnaToEmblProteinRef.setMap(dnaToProteinMapping); + dnaToProteinMapping.setMappedFromId(proteinId); + dna.addDBRef(dnaToEmblProteinRef); } } } @@ -650,7 +610,7 @@ public class EmblEntry } /** - * Returns the CDS positions as a list of [start, end, start, end...] + * Returns the CDS positions as a single array of [start, end, start, end...] * positions. If on the reverse strand, these will be in descending order. * * @param feature @@ -658,51 +618,68 @@ public class EmblEntry */ protected int[] getCdsRanges(EmblFeature feature) { - if (feature.locations == null) + if (feature.location == null) { return new int[] {}; } - int cdsBoundaryCount = 0; // count of all start/stop locations - int[][] cdsLocations = new int[feature.locations.size()][]; - int locationNumber = 0; - for (EmblFeatureLocations loc : feature.locations) + + try { - int[] locationRanges = loc.getElementRanges(accession); - cdsLocations[locationNumber++] = locationRanges; - cdsBoundaryCount += locationRanges.length; - } - int[] cdsRanges = new int[cdsBoundaryCount]; - int copyTo = 0; - for (int[] ranges : cdsLocations) + List ranges = DnaUtils.parseLocation(feature.location); + return listToArray(ranges); + } catch (ParseException e) { - System.arraycopy(ranges, 0, cdsRanges, copyTo, ranges.length); - copyTo += ranges.length; + Cache.log.warn(String.format( + "Not parsing inexact CDS location %s in ENA %s", + feature.location, this.accession)); + return new int[] {}; } - return cdsRanges; + } + /** + * Converts a list of [start, end] ranges to a single array of [start, end, + * start, end ...] + * + * @param ranges + * @return + */ + int[] listToArray(List ranges) + { + int[] result = new int[ranges.size() * 2]; + int i = 0; + for (int[] range : ranges) + { + result[i++] = range[0]; + result[i++] = range[1]; + } + return result; } /** - * truncate the last exon interval to the prlength'th codon + * Truncates (if necessary) the exon intervals to match 3 times the length of + * the protein; also accepts 3 bases longer (for stop codon not included in + * protein) * - * @param prlength + * @param proteinLength * @param exon - * @return new exon + * an array of [start, end, start, end...] intervals + * @return the same array (if unchanged) or a truncated copy */ - static int[] adjustForProteinLength(int prlength, int[] exon) + static int[] adjustForProteinLength(int proteinLength, int[] exon) { - if (prlength <= 0 || exon == null) + if (proteinLength <= 0 || exon == null) { return exon; } - int desiredCdsLength = prlength * 3; + int expectedCdsLength = proteinLength * 3; int exonLength = MappingUtils.getLength(Arrays.asList(exon)); /* - * assuming here exon might include stop codon in addition to protein codons + * if exon length matches protein, or is shorter, or longer by the + * length of a stop codon (3 bases), then leave it unchanged */ - if (desiredCdsLength == exonLength - || desiredCdsLength == exonLength - 3) + if (expectedCdsLength >= exonLength + || expectedCdsLength == exonLength - 3) { return exon; } @@ -716,11 +693,11 @@ public class EmblEntry for (int x = 0; x < exon.length; x += 2) { cdspos += Math.abs(exon[x + 1] - exon[x]) + 1; - if (desiredCdsLength <= cdspos) + if (expectedCdsLength <= cdspos) { // advanced beyond last codon. sxpos = x; - if (desiredCdsLength != cdspos) + if (expectedCdsLength != cdspos) { // System.err // .println("Truncating final exon interval on region by " @@ -733,11 +710,11 @@ public class EmblEntry */ if (exon[x + 1] >= exon[x]) { - endxon = exon[x + 1] - cdspos + desiredCdsLength; + endxon = exon[x + 1] - cdspos + expectedCdsLength; } else { - endxon = exon[x + 1] + cdspos - desiredCdsLength; + endxon = exon[x + 1] + cdspos - expectedCdsLength; } break; } @@ -754,4 +731,124 @@ public class EmblEntry } return exon; } + + public String getSequenceVersion() + { + return sequenceVersion; + } + + public void setSequenceVersion(String sequenceVersion) + { + this.sequenceVersion = sequenceVersion; + } + + public String getSequenceLength() + { + return sequenceLength; + } + + public void setSequenceLength(String sequenceLength) + { + this.sequenceLength = sequenceLength; + } + + public String getEntryVersion() + { + return entryVersion; + } + + public void setEntryVersion(String entryVersion) + { + this.entryVersion = entryVersion; + } + + public String getMoleculeType() + { + return moleculeType; + } + + public void setMoleculeType(String moleculeType) + { + this.moleculeType = moleculeType; + } + + public String getTopology() + { + return topology; + } + + public void setTopology(String topology) + { + this.topology = topology; + } + + public String getTaxonomicDivision() + { + return taxonomicDivision; + } + + public void setTaxonomicDivision(String taxonomicDivision) + { + this.taxonomicDivision = taxonomicDivision; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + + public String getFirstPublicDate() + { + return firstPublicDate; + } + + public void setFirstPublicDate(String firstPublicDate) + { + this.firstPublicDate = firstPublicDate; + } + + public String getFirstPublicRelease() + { + return firstPublicRelease; + } + + public void setFirstPublicRelease(String firstPublicRelease) + { + this.firstPublicRelease = firstPublicRelease; + } + + public String getLastUpdatedDate() + { + return lastUpdatedDate; + } + + public void setLastUpdatedDate(String lastUpdatedDate) + { + this.lastUpdatedDate = lastUpdatedDate; + } + + public String getLastUpdatedRelease() + { + return lastUpdatedRelease; + } + + public void setLastUpdatedRelease(String lastUpdatedRelease) + { + this.lastUpdatedRelease = lastUpdatedRelease; + } + + public String getDataClass() + { + return dataClass; + } + + public void setDataClass(String dataClass) + { + this.dataClass = dataClass; + } } diff --git a/src/jalview/datamodel/xdb/embl/EmblFeature.java b/src/jalview/datamodel/xdb/embl/EmblFeature.java index 7e503c9..51d740b 100644 --- a/src/jalview/datamodel/xdb/embl/EmblFeature.java +++ b/src/jalview/datamodel/xdb/embl/EmblFeature.java @@ -37,7 +37,7 @@ public class EmblFeature Vector qualifiers; - Vector locations; + String location; /** * @return the dbRefs @@ -57,20 +57,19 @@ public class EmblFeature } /** - * @return the locations + * @return the location */ - public Vector getLocations() + public String getLocation() { - return locations; + return location; } /** - * @param locations - * the locations to set + * @param loc */ - public void setLocations(Vector locations) + public void setLocation(String loc) { - this.locations = locations; + this.location = loc; } /** diff --git a/src/jalview/datamodel/xdb/embl/EmblFeatureLocElement.java b/src/jalview/datamodel/xdb/embl/EmblFeatureLocElement.java deleted file mode 100644 index 134ce9e..0000000 --- a/src/jalview/datamodel/xdb/embl/EmblFeatureLocElement.java +++ /dev/null @@ -1,125 +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 . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ -package jalview.datamodel.xdb.embl; - -/** - * Data model for a feature/location/locationElement read from an EMBL query - * reply - * - * @see embl_mapping.xml - */ -public class EmblFeatureLocElement -{ - String type; - - String accession; - - String version; - - boolean complement; - - BasePosition basePositions[]; - - /** - * @return the accession - */ - public String getAccession() - { - return accession; - } - - /** - * @param accession - * the accession to set - */ - public void setAccession(String accession) - { - this.accession = accession; - } - - /** - * @return the basePositions - */ - public BasePosition[] getBasePositions() - { - return basePositions; - } - - /** - * @param basePositions - * the basePositions to set - */ - public void setBasePositions(BasePosition[] basePositions) - { - this.basePositions = basePositions; - } - - /** - * @return the complement - */ - public boolean isComplement() - { - return complement; - } - - /** - * @param complement - * the complement to set - */ - public void setComplement(boolean complement) - { - this.complement = complement; - } - - /** - * @return the type - */ - public String getType() - { - return type; - } - - /** - * @param type - * the type to set - */ - public void setType(String type) - { - this.type = type; - } - - /** - * @return the version - */ - public String getVersion() - { - return version; - } - - /** - * @param version - * the version to set - */ - public void setVersion(String version) - { - this.version = version; - } -} diff --git a/src/jalview/datamodel/xdb/embl/EmblFeatureLocations.java b/src/jalview/datamodel/xdb/embl/EmblFeatureLocations.java deleted file mode 100644 index 9774004..0000000 --- a/src/jalview/datamodel/xdb/embl/EmblFeatureLocations.java +++ /dev/null @@ -1,191 +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 . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ -package jalview.datamodel.xdb.embl; - -import jalview.bin.Cache; -import jalview.util.ArrayUtils; - -import java.util.Arrays; -import java.util.Vector; - -/** - * Data model for a <location> child element of a <feature> read - * from an EMBL query reply - * - * @see embl_mapping.xml - * @see http://www.insdc.org/files/feature_table.html#3.4.2 - */ -public class EmblFeatureLocations -{ - Vector locElements; - - String locationType; - - boolean locationComplement; - - /** - * @return the locationComplement - */ - public boolean isLocationComplement() - { - return locationComplement; - } - - /** - * @param locationComplement - * the locationComplement to set - */ - public void setLocationComplement(boolean locationComplement) - { - this.locationComplement = locationComplement; - } - - /** - * @return the locationType - */ - public String getLocationType() - { - return locationType; - } - - /** - * @param locationType - * the locationType to set - */ - public void setLocationType(String locationType) - { - this.locationType = locationType; - } - - /** - * @return the locElements - */ - public Vector getLocElements() - { - return locElements; - } - - /** - * @param locElements - * the locElements to set - */ - public void setLocElements(Vector locElements) - { - this.locElements = locElements; - } - - /** - * Return all location elements as start-end pairs (without accessions) TODO: - * pass back complement and 'less than or more than' range information Note: - * do not use this since it throws away any accessionIds associated with each - * location! - * - * @return int[] { start1, end1, ... } - */ - public int[] getElementRanges() - { - return getElementRanges(null); - } - - /** - * Return all location elements concerning given accession as start-end pairs. - * If the CDS feature is on the forward strand, then start <= end, if on the - * reverse strand then start > end. - * - * @param accession - * the accession string for which locations are requested, or null - * for all locations - * @return int[] { start1, end1, ... } - */ - int[] getElementRanges(String accession) - { - int sepos = 0; - int[] se = new int[locElements.size() * 2]; - if ("single".equalsIgnoreCase(locationType) - || "join".equalsIgnoreCase(locationType)) - { - for (EmblFeatureLocElement loce : locElements) - { - if (accession == null || loce.accession != null - && accession.equals(loce.accession)) - { - BasePosition bp[] = loce.getBasePositions(); - if (bp.length == 2) - { - try - { - int start = Integer.parseInt(bp[0].getPos()); - int end = Integer.parseInt(bp[1].getPos()); - se[sepos++] = start; - se[sepos++] = end; - } catch (NumberFormatException e) - { - System.err - .println("format error in EMBL CDS location basePosition: " - + e.getMessage()); - } - } - else - { - System.err - .println("format error in EMBL CDS location, basePosition count = " - + bp.length); - } - } - } - } - else if (locationType != null) - { - if (Cache.log != null) - { - Cache.log - .error("EmblFeatureLocations.getElementRanges cannot deal with locationType=='" - + locationType + "'"); - } - else - { - System.err - .println("EmblFeatureLocations.getElementRanges cannot deal with locationType=='" - + locationType + "'"); - } - } - - if (sepos != se.length) - { - /* - * we failed to parse something - trim off null values - */ - se = Arrays.copyOf(se, sepos); - } - - /* - * If on the complement, reverse the ranges to [end, start, ...end1, start1]. - * For an example of a joined complement, see (tRNA feature) CAGL0B00165r on - * http://www.ebi.ac.uk/ena/data/view/CR380948&display=xml - * http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/embl/CR380948/emblxml - */ - if (locationComplement) - { - ArrayUtils.reverseIntArray(se); - } - return se; - } -} diff --git a/src/jalview/datamodel/xdb/embl/EmblSequence.java b/src/jalview/datamodel/xdb/embl/EmblSequence.java index 2a6fa84..92c424b 100644 --- a/src/jalview/datamodel/xdb/embl/EmblSequence.java +++ b/src/jalview/datamodel/xdb/embl/EmblSequence.java @@ -27,12 +27,8 @@ package jalview.datamodel.xdb.embl; */ public class EmblSequence { - String version; - String sequence; - String type; - /** * @return the sequence */ @@ -47,40 +43,7 @@ public class EmblSequence */ public void setSequence(String sequence) { - this.sequence = sequence; - } - - /** - * @return the type - */ - public String getType() - { - return type; - } - - /** - * @param type - * the type to set - */ - public void setType(String type) - { - this.type = type; - } - - /** - * @return the version - */ - public String getVersion() - { - return version; - } - - /** - * @param version - * the version to set - */ - public void setVersion(String version) - { - this.version = version; + // remove spaces introduced by unmarshalling of newline characters + this.sequence = sequence.replace(" ", ""); } } diff --git a/src/jalview/ext/ensembl/EnsemblGene.java b/src/jalview/ext/ensembl/EnsemblGene.java index b320e83..6f3c062 100644 --- a/src/jalview/ext/ensembl/EnsemblGene.java +++ b/src/jalview/ext/ensembl/EnsemblGene.java @@ -119,7 +119,10 @@ public class EnsemblGene extends EnsemblSeqProxy * fetch the gene sequence(s) with features and xrefs */ AlignmentI geneAlignment = super.getSequenceRecords(geneId); - + if (geneAlignment == null) + { + continue; + } if (geneAlignment.getHeight() == 1) { getTranscripts(geneAlignment, geneId); @@ -175,7 +178,8 @@ public class EnsemblGene extends EnsemblSeqProxy */ else { - List ids = new EnsemblSymbol(getDomain()).getIds(acc); + List ids = new EnsemblSymbol(getDomain(), getDbSource(), + getDbVersion()).getIds(acc); for (String geneId : ids) { if (!geneIds.contains(geneId)) @@ -197,7 +201,8 @@ public class EnsemblGene extends EnsemblSeqProxy */ protected String getGeneIdentifiersForName(String query) { - List ids = new EnsemblSymbol(getDomain()).getIds(query); + List ids = new EnsemblSymbol(getDomain(), getDbSource(), + getDbVersion()).getIds(query); if (ids != null) { for (String id : ids) diff --git a/src/jalview/ext/ensembl/EnsemblInfo.java b/src/jalview/ext/ensembl/EnsemblInfo.java new file mode 100644 index 0000000..88b5ac4 --- /dev/null +++ b/src/jalview/ext/ensembl/EnsemblInfo.java @@ -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 and not {"error":"Database is unavailable"} + */ + 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; + } + +} diff --git a/src/jalview/ext/ensembl/EnsemblRestClient.java b/src/jalview/ext/ensembl/EnsemblRestClient.java index 6a564f1..72efdc1 100644 --- a/src/jalview/ext/ensembl/EnsemblRestClient.java +++ b/src/jalview/ext/ensembl/EnsemblRestClient.java @@ -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,25 @@ 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.6"; - protected final static String ENSEMBL_GENOMES_REST = "http://rest.ensemblgenomes.org"; + private static final String LATEST_ENSEMBL_REST_VERSION = "4.6"; + + private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log"; + + private static Map 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 +56,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(); + 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,23 +82,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher */ public EnsemblRestClient(String d) { - domain = d; - } - - /** - * Returns the domain name to query e.g. http://rest.ensembl.org or - * http://rest.ensemblgenomes.org - * - * @return - */ - String getDomain() - { - return domain; - } - - void setDomain(String d) - { - domain = d; + setDomain(d); } /** @@ -157,30 +157,40 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher protected abstract String getResponseMimeType(); /** - * Tries to connect to Ensembl's REST 'ping' endpoint, and returns true if - * successful, else false + * Checks Ensembl's REST 'ping' endpoint, and returns true if response + * indicates available, else false * + * @see http://rest.ensembl.org/documentation/info/ping * @return */ private boolean checkEnsembl() { + HttpURLConnection conn = null; try { // note this format works for both ensembl and ensemblgenomes // info/ping.json works for ensembl only (March 2016) URL ping = new URL(getDomain() + "/info/ping?content-type=application/json"); - HttpURLConnection conn = (HttpURLConnection) ping.openConnection(); - int rc = conn.getResponseCode(); - conn.disconnect(); - if (rc >= 200 && rc < 300) - { - return true; - } + + /* + * expect {"ping":1} if ok + */ + BufferedReader br = getHttpResponse(ping, null); + JSONParser jp = new JSONParser(); + JSONObject val = (JSONObject) jp.parse(br); + String pingString = val.get("ping").toString(); + return pingString != null; } catch (Throwable t) { System.err.println("Error connecting to " + PING_URL + ": " + t.getMessage()); + } finally + { + if (conn != null) + { + conn.disconnect(); + } } return false; } @@ -222,7 +232,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", @@ -284,13 +294,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) { @@ -300,46 +311,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; } /** @@ -378,4 +407,103 @@ 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, see %s", + getDbSource(), expected, version, REST_CHANGE_LOG)); + } + 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(); + } + } diff --git a/src/jalview/ext/ensembl/EnsemblSeqProxy.java b/src/jalview/ext/ensembl/EnsemblSeqProxy.java index fb81e66..5fccedd 100644 --- a/src/jalview/ext/ensembl/EnsemblSeqProxy.java +++ b/src/jalview/ext/ensembl/EnsemblSeqProxy.java @@ -5,7 +5,6 @@ import jalview.analysis.Dna; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; -import jalview.datamodel.DBRefSource; import jalview.datamodel.Mapping; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; @@ -277,11 +276,10 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient { // clunky: ensure Uniprot xref if we have one is on mapped sequence SequenceI ds = proteinSeq.getDatasetSequence(); - ds.setSourceDBRef(proteinSeq.getSourceDBRef()); - + // TODO: Verify ensp primary ref is on proteinSeq.getDatasetSequence() 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); /* @@ -310,24 +308,19 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient seq = seq.getDatasetSequence(); } - EnsemblXref xrefFetcher = new EnsemblXref(getDomain()); + EnsemblXref xrefFetcher = new EnsemblXref(getDomain(), getDbSource(), + getEnsemblDataVersion()); List xrefs = xrefFetcher.getCrossReferences(seq.getName()); for (DBRefEntry xref : xrefs) { seq.addDBRef(xref); - /* - * Save any Uniprot xref to be the reference for SIFTS mapping - */ - if (DBRefSource.UNIPROT.equals(xref.getSource())) - { - seq.setSourceDBRef(xref); - } } /* * 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); } @@ -386,7 +379,9 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient if (ids.contains(name) || ids.contains(name.replace("ENSP", "ENST"))) { - DBRefUtils.parseToDbRef(sq, DBRefSource.ENSEMBL, "0", name); + DBRefEntry dbref = DBRefUtils.parseToDbRef(sq, getDbSource(), + getEnsemblDataVersion(), name); + sq.addDBRef(dbref); } } if (alignment == null) @@ -617,6 +612,10 @@ public abstract class EnsemblSeqProxy extends EnsemblRestClient SequenceFeature copy = new SequenceFeature(sf); copy.setBegin(Math.min(mappedRange[0], mappedRange[1])); copy.setEnd(Math.max(mappedRange[0], mappedRange[1])); + if (".".equals(copy.getFeatureGroup())) + { + copy.setFeatureGroup(getDbSource()); + } targetSequence.addSequenceFeature(copy); /* diff --git a/src/jalview/ext/ensembl/EnsemblSequenceFetcher.java b/src/jalview/ext/ensembl/EnsemblSequenceFetcher.java index 9a4952e..dd1739b 100644 --- a/src/jalview/ext/ensembl/EnsemblSequenceFetcher.java +++ b/src/jalview/ext/ensembl/EnsemblSequenceFetcher.java @@ -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/ext/ensembl/EnsemblSymbol.java b/src/jalview/ext/ensembl/EnsemblSymbol.java index 1c47f11..b8c8c54 100644 --- a/src/jalview/ext/ensembl/EnsemblSymbol.java +++ b/src/jalview/ext/ensembl/EnsemblSymbol.java @@ -25,11 +25,13 @@ public class EnsemblSymbol extends EnsemblXref /** * Constructor given the target domain to fetch data from * - * @param d + * @param domain + * @param dbName + * @param dbVersion */ - public EnsemblSymbol(String d) + public EnsemblSymbol(String domain, String dbName, String dbVersion) { - super(d); + super(domain, dbName, dbVersion); } /** diff --git a/src/jalview/ext/ensembl/EnsemblXref.java b/src/jalview/ext/ensembl/EnsemblXref.java index fa86865..313572f 100644 --- a/src/jalview/ext/ensembl/EnsemblXref.java +++ b/src/jalview/ext/ensembl/EnsemblXref.java @@ -29,20 +29,25 @@ class EnsemblXref extends EnsemblRestClient private static final String GO_GENE_ONTOLOGY = "GO"; + private String dbName = "ENSEMBL (xref)"; + /** * Constructor given the target domain to fetch data from * * @param d */ - public EnsemblXref(String d) + public EnsemblXref(String d, String dbSource, String version) { super(d); + dbName = dbSource; + xrefVersion = dbSource + ":" + version; + } @Override public String getDbName() { - return "ENSEMBL (xref)"; + return dbName; } @Override @@ -152,7 +157,7 @@ class EnsemblXref extends EnsemblRestClient if (dbName != null && id != null) { dbName = DBRefUtils.getCanonicalName(dbName); - DBRefEntry dbref = new DBRefEntry(dbName, "0", id); + DBRefEntry dbref = new DBRefEntry(dbName, getXRefVersion(), id); result.add(dbref); } } @@ -163,6 +168,18 @@ class EnsemblXref extends EnsemblRestClient return result; } + private String xrefVersion = "ENSEMBL:0"; + + /** + * version string for Xrefs - for 2.10, hardwired for ENSEMBL:0 + * + * @return + */ + public String getXRefVersion() + { + return xrefVersion; + } + /** * Returns the URL for the REST endpoint to fetch all cross-references for an * identifier. Note this may return protein cross-references for nucleotide. diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 3f0847b..2ccf118 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -57,7 +57,6 @@ import org.jmol.api.JmolStatusListener; import org.jmol.api.JmolViewer; import org.jmol.c.CBK; import org.jmol.script.T; -import org.jmol.viewer.JC; import org.jmol.viewer.Viewer; public abstract class JalviewJmolBinding extends AAStructureBindingModel @@ -170,12 +169,9 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel public void closeViewer() { - viewer.acm.setModeMouse(JC.MOUSE_NONE); // remove listeners for all structures in viewer getSsm().removeStructureViewerListener(this, this.getPdbFile()); - // and shut down jmol - viewer.evalStringQuiet("zap"); - viewer.setJmolStatusListener(null); + viewer.dispose(); lastCommand = null; viewer = null; releaseUIResources(); diff --git a/src/jalview/ext/jmol/JmolParser.java b/src/jalview/ext/jmol/JmolParser.java index 0cbd620..cef552f 100644 --- a/src/jalview/ext/jmol/JmolParser.java +++ b/src/jalview/ext/jmol/JmolParser.java @@ -22,16 +22,18 @@ package jalview.ext.jmol; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; -import jalview.datamodel.DBRefSource; +import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.io.FileParse; import jalview.io.StructureFile; import jalview.schemes.ResidueProperties; -import jalview.structure.StructureViewSettings; +import jalview.structure.StructureImportSettings; +import jalview.util.Format; import jalview.util.MessageManager; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; @@ -59,15 +61,13 @@ public class JmolParser extends StructureFile implements JmolStatusListener { Viewer viewer = null; - public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr, - boolean externalSecStr, String inFile, String type) + public JmolParser(String inFile, String type) throws IOException { super(inFile, type); } - public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr, - boolean externalSecStr, FileParse fp) throws IOException + public JmolParser(FileParse fp) throws IOException { super(fp); } @@ -87,15 +87,6 @@ public class JmolParser extends StructureFile implements JmolStatusListener @Override public void parse() throws IOException { - String dataName = getDataName(); - if (dataName.endsWith(".cif")) - { - setDbRefType(DBRefSource.MMCIF); - } - else - { - setDbRefType(DBRefSource.PDB); - } setChains(new Vector()); Viewer jmolModel = getJmolData(); jmolModel.openReader(getDataName(), getDataName(), getReader()); @@ -106,6 +97,18 @@ public class JmolParser extends StructureFile implements JmolStatusListener */ if (jmolModel.ms.mc > 0) { + // ideally we do this + // try + // { + // setStructureFileType(jmolModel.evalString("show _fileType")); + // } catch (Exception q) + // { + // } + // ; + // instead, we distinguish .cif from non-.cif by filename + setStructureFileType(getDataName().toLowerCase().endsWith(".cif") ? PDBEntry.Type.MMCIF + .toString() : "PDB"); + transformJmolModelToJalview(jmolModel.ms); } } @@ -172,7 +175,7 @@ public class JmolParser extends StructureFile implements JmolStatusListener if (getId() == null) { - setId(inFile.getName()); + setId(safeName(getDataName())); } for (PDBChain chain : getChains()) { @@ -186,7 +189,7 @@ public class JmolParser extends StructureFile implements JmolStatusListener prot.add(chainseq); } - if (StructureViewSettings.isPredictSecondaryStructure()) + if (StructureImportSettings.isProcessSecondaryStructure()) { createAnnotation(chainseq, chain, ms.at); } @@ -204,30 +207,99 @@ public class JmolParser extends StructureFile implements JmolStatusListener private List convertSignificantAtoms(ModelSet ms) { List significantAtoms = new ArrayList(); + HashMap chainTerMap = new HashMap(); + org.jmol.modelset.Atom prevAtom = null; for (org.jmol.modelset.Atom atom : ms.at) { if (atom.getAtomName().equalsIgnoreCase("CA") || atom.getAtomName().equalsIgnoreCase("P")) { + if (!atomValidated(atom, prevAtom, chainTerMap)) + { + continue; + } Atom curAtom = new Atom(atom.x, atom.y, atom.z); curAtom.atomIndex = atom.getIndex(); curAtom.chain = atom.getChainIDStr(); - curAtom.insCode = atom.group.getInsertionCode(); + curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' ' + : atom.group.getInsertionCode(); curAtom.name = atom.getAtomName(); curAtom.number = atom.getAtomNumber(); curAtom.resName = atom.getGroup3(true); curAtom.resNumber = atom.getResno(); curAtom.occupancy = ms.occupancies != null ? ms.occupancies[atom .getIndex()] : Float.valueOf(atom.getOccupancy100()); - curAtom.resNumIns = "" + curAtom.resNumber + curAtom.insCode; + String fmt = new Format("%4i").form(curAtom.resNumber); + curAtom.resNumIns = (fmt + curAtom.insCode); curAtom.tfactor = atom.getBfactor100() / 100f; curAtom.type = 0; - significantAtoms.add(curAtom); + // significantAtoms.add(curAtom); + // ignore atoms from subsequent models + if (!significantAtoms.contains(curAtom)) + { + significantAtoms.add(curAtom); + } + prevAtom = atom; } } return significantAtoms; } + private boolean atomValidated(org.jmol.modelset.Atom curAtom, + org.jmol.modelset.Atom prevAtom, + HashMap chainTerMap) + { + // System.out.println("Atom: " + curAtom.getAtomNumber() + // + " Last atom index " + curAtom.group.lastAtomIndex); + if (chainTerMap == null || prevAtom == null) + { + return true; + } + String curAtomChId = curAtom.getChainIDStr(); + String prevAtomChId = prevAtom.getChainIDStr(); + // new chain encoutered + if (!prevAtomChId.equals(curAtomChId)) + { + // On chain switch add previous chain termination to xTerMap if not exists + if (!chainTerMap.containsKey(prevAtomChId)) + { + chainTerMap.put(prevAtomChId, prevAtom); + } + // if current atom belongs to an already terminated chain and the resNum + // diff < 5 then mark as valid and update termination Atom + if (chainTerMap.containsKey(curAtomChId)) + { + if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno()) + { + return false; + } + if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5) + { + chainTerMap.put(curAtomChId, curAtom); + return true; + } + return false; + } + } + // atom with previously terminated chain encountered + else if (chainTerMap.containsKey(curAtomChId)) + { + if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno()) + { + return false; + } + if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5) + { + chainTerMap.put(curAtomChId, curAtom); + return true; + } + return false; + } + // HETATM with resNum jump > 2 + return !(curAtom.isHetero() && ((curAtom.getResno() - prevAtom + .getResno()) > 2)); + } + private void createAnnotation(SequenceI sequence, PDBChain chain, org.jmol.modelset.Atom[] jmolAtoms) { diff --git a/src/jalview/ext/paradise/Annotate3D.java b/src/jalview/ext/paradise/Annotate3D.java index 85c84ab..5b75206 100644 --- a/src/jalview/ext/paradise/Annotate3D.java +++ b/src/jalview/ext/paradise/Annotate3D.java @@ -45,13 +45,20 @@ import org.json.simple.parser.ParseException; * * @author jimp * - * History: v1.0 revised from original due to refactoring of - * paradise-ubmc.u-strasbg.fr/webservices/annotate3d to - * http://arn-ibmc.in2p3.fr/api/compute/2d?tool=rnaview + * @version v1.0 revised from original due to refactoring of + * paradise-ubmc.u-strasbg.fr/webservices/annotate3d to + * http://arn-ibmc.in2p3.fr/api/compute/2d?tool=rnaview
        + * See also testing URL from fjossinet:
        + * http://charn2-ibmc.u-strasbg.fr:8080/api/compute/2d
        + * If in doubt, check against the REST client at: + * https://github.com/fjossinet/RNA-Science + * -Toolbox/blob/master/pyrna/restclient.py */ public class Annotate3D { - private static String twoDtoolsURL = "http://arn-ibmc.in2p3.fr/api/compute/2d"; + // also test with + // "http://charn2-ibmc.u-strasbg.fr:8080/api/compute/2d"; + private static String twoDtoolsURL = "http://arn-ibmc.in2p3.fr/api/compute/2d?tool=rnaview"; private static ContentHandler createContentHandler() { diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 1ce0d2b..944ef52 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -101,17 +101,13 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel private String lastCommand; - private boolean loadedInline; - - /** + /* * current set of model filenames loaded */ String[] modelFileNames = null; String lastHighlightCommand; - private List lastReply; - /* * incremented every time a load notification is successfully handled - * lightweight mechanism for other threads to detect when they can start @@ -617,7 +613,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel if (lastCommand == null || !lastCommand.equals(command)) { // trim command or it may never find a match in the replyLog!! - lastReply = viewer.sendChimeraCommand(command.trim(), logResponse); + List lastReply = viewer.sendChimeraCommand(command.trim(), + logResponse); if (logResponse && debug) { log("Response from command ('" + command + "') was:\n" + lastReply); @@ -715,17 +712,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel // End StructureListener // ////////////////////////// - public Color getColour(int atomIndex, int pdbResNum, String chain, - String pdbfile) - { - if (getModelNum(pdbfile) < 0) - { - return null; - } - log("get model / residue colour attribute unimplemented"); - return null; - } - /** * returns the current featureRenderer that should be used to colour the * structures @@ -795,15 +781,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } /** - * map from string to applet - */ - public Map getRegistryInfo() - { - // TODO Auto-generated method stub - return null; - } - - /** * returns the current sequenceRenderer that should be used to colour the * structures * @@ -815,20 +792,18 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel AlignmentViewPanel alignment); /** - * Construct and send a command to highlight zero, one or more atoms. - * - *

        -   * Done by generating a command like (to 'highlight' positions 44 and 46)
        -   *   show #0:44,46.C
        -   * 
        + * Construct and send a command to highlight zero, one or more atoms. We do + * this by sending an "rlabel" command to show the residue label at that + * position. */ @Override public void highlightAtoms(List atoms) { - if (atoms == null) + if (atoms == null || atoms.size() == 0) { return; } + StringBuilder cmd = new StringBuilder(128); boolean first = true; boolean found = false; @@ -843,7 +818,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { if (first) { - cmd.append("show #").append(cms.get(0).getModelNumber()) + cmd.append("rlabel #").append(cms.get(0).getModelNumber()) .append(":"); } else @@ -851,7 +826,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel cmd.append(","); } first = false; - cmd.append(cms.get(0).getModelNumber()).append(":"); cmd.append(pdbResNum); if (!chain.equals(" ")) { @@ -863,19 +837,24 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel String command = cmd.toString(); /* - * Avoid repeated commands for the same residue + * avoid repeated commands for the same residue */ if (command.equals(lastHighlightCommand)) { return; } - viewerCommandHistory(false); + /* + * unshow the label for the previous residue + */ + if (lastHighlightCommand != null) + { + viewer.sendChimeraCommand("~" + lastHighlightCommand, false); + } if (found) { - viewer.sendChimeraCommand(command.toString(), false); + viewer.sendChimeraCommand(command, false); } - viewerCommandHistory(true); this.lastHighlightCommand = command; } diff --git a/src/jalview/fts/api/FTSDataColumnI.java b/src/jalview/fts/api/FTSDataColumnI.java index 7741d73..80990b4 100644 --- a/src/jalview/fts/api/FTSDataColumnI.java +++ b/src/jalview/fts/api/FTSDataColumnI.java @@ -93,18 +93,18 @@ public interface FTSDataColumnI public boolean isVisibleByDefault(); /** - * Returns the data column's data type class + * Returns the data column's FTS data column group * - * @return the Class for the data column's data type + * @return the FTSDataColumnGroupI for the column */ - public Class getDataColumnClass(); + public FTSDataColumnGroupI getGroup(); /** - * Returns the data colum's FTS data column group + * Returns the data columns data type POJO * - * @return the FTSDataColumnGroupI for the column + * @return the DataTypeI for the column */ - public FTSDataColumnGroupI getGroup(); + public DataTypeI getDataType(); /** * This interface provides a model for the dynamic data column group @@ -133,4 +133,29 @@ public interface FTSDataColumnI */ 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 index 2266ca0..3701c76 100644 --- a/src/jalview/fts/api/FTSRestClientI.java +++ b/src/jalview/fts/api/FTSRestClientI.java @@ -115,7 +115,7 @@ public interface FTSRestClientI * * @return list of columns to display by default */ - public Collection getAllDefaulDisplayedDataColumns(); + public Collection getAllDefaultDisplayedFTSDataColumns(); /** * Return list of FTSDataColumnI objects that can be used to perform a search diff --git a/src/jalview/fts/api/GFTSPanelI.java b/src/jalview/fts/api/GFTSPanelI.java index ce63576..f86c3bc 100644 --- a/src/jalview/fts/api/GFTSPanelI.java +++ b/src/jalview/fts/api/GFTSPanelI.java @@ -21,6 +21,8 @@ package jalview.fts.api; +import java.util.Map; + import javax.swing.JTable; /** @@ -127,4 +129,12 @@ public interface GFTSPanelI * @param isEnabled */ public void setNextPageButtonEnabled(boolean isEnabled); + + /** + * The HashMap used to store user preferences for summary table columns, + * window size and position + * + * @return + */ + public Map getTempUserPrefs(); } diff --git a/src/jalview/fts/core/DecimalFormatTableCellRenderer.java b/src/jalview/fts/core/DecimalFormatTableCellRenderer.java new file mode 100644 index 0000000..3642721 --- /dev/null +++ b/src/jalview/fts/core/DecimalFormatTableCellRenderer.java @@ -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 diff --git a/src/jalview/fts/core/FTSDataColumnPreferences.java b/src/jalview/fts/core/FTSDataColumnPreferences.java index cddcc8e..eb7455e 100644 --- a/src/jalview/fts/core/FTSDataColumnPreferences.java +++ b/src/jalview/fts/core/FTSDataColumnPreferences.java @@ -23,6 +23,7 @@ package jalview.fts.core; import jalview.fts.api.FTSDataColumnI; import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI; import jalview.fts.api.FTSRestClientI; +import jalview.fts.service.pdb.PDBFTSRestClient; import java.util.ArrayList; import java.util.Collection; @@ -67,11 +68,12 @@ public class FTSDataColumnPreferences extends JScrollPane FTSRestClientI ftsRestClient) { this.ftsRestClient = ftsRestClient; - Collection defaultCols = ftsRestClient - .getAllDefaulDisplayedDataColumns(); - - structSummaryColumns.addAll(defaultCols); - + if (source.equals(PreferenceSource.STRUCTURE_CHOOSER) + || source.equals(PreferenceSource.PREFERENCES)) + { + structSummaryColumns = ((PDBFTSRestClient) ftsRestClient) + .getAllDefaultDisplayedStructureDataColumns(); + } allFTSDataColumns.addAll(ftsRestClient.getAllFTSDataColumns()); tbl_FTSDataColumnPrefs.setAutoCreateRowSorter(true); @@ -109,7 +111,7 @@ public class FTSDataColumnPreferences extends JScrollPane { case SEARCH_SUMMARY: data[x++] = new Object[] { - ftsRestClient.getAllDefaulDisplayedDataColumns() + ftsRestClient.getAllDefaultDisplayedFTSDataColumns() .contains(field), field.getName(), field.getGroup() }; break; @@ -119,7 +121,7 @@ public class FTSDataColumnPreferences extends JScrollPane break; case PREFERENCES: data[x++] = new Object[] { field.getName(), - ftsRestClient.getAllDefaulDisplayedDataColumns() + ftsRestClient.getAllDefaultDisplayedFTSDataColumns() .contains(field), structSummaryColumns.contains(field) }; break; @@ -300,7 +302,7 @@ public class FTSDataColumnPreferences extends JScrollPane if (currentSource == PreferenceSource.SEARCH_SUMMARY) { updatePrefs(ftsRestClient - .getAllDefaulDisplayedDataColumns(), ftsDataColumn, + .getAllDefaultDisplayedFTSDataColumns(), ftsDataColumn, selected); } else if (currentSource == PreferenceSource.STRUCTURE_CHOOSER) @@ -312,7 +314,7 @@ public class FTSDataColumnPreferences extends JScrollPane if (col == 1) { updatePrefs(ftsRestClient - .getAllDefaulDisplayedDataColumns(), ftsDataColumn, + .getAllDefaultDisplayedFTSDataColumns(), ftsDataColumn, selected); } else if (col == 2) diff --git a/src/jalview/fts/core/FTSRestClient.java b/src/jalview/fts/core/FTSRestClient.java index 2f24a01..00a081b 100644 --- a/src/jalview/fts/core/FTSRestClient.java +++ b/src/jalview/fts/core/FTSRestClient.java @@ -143,21 +143,62 @@ public abstract class FTSRestClient implements FTSRestClientI } @Override - public Class getDataColumnClass() + public DataTypeI getDataType() { - String classString = lineData[2]; - classString = classString.toUpperCase(); - switch (classString) + final String[] dataTypeString = lineData[2].split("\\|"); + final String classString = dataTypeString[0].toUpperCase(); + + return new DataTypeI() { - case "INT": - case "INTEGER": - return Integer.class; - case "DOUBLE": - return Double.class; - case "STRING": - default: - return String.class; - } + + @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 @@ -228,6 +269,7 @@ public abstract class FTSRestClient implements FTSRestClientI && this.getGroup().equals(that.getGroup()); } + }; dataColumns.add(dataCol); @@ -325,7 +367,7 @@ public abstract class FTSRestClient implements FTSRestClientI } @Override - public Collection getAllDefaulDisplayedDataColumns() + public Collection getAllDefaultDisplayedFTSDataColumns() { if (defaulDisplayedDataColumns == null || defaulDisplayedDataColumns.isEmpty()) diff --git a/src/jalview/fts/core/FTSRestResponse.java b/src/jalview/fts/core/FTSRestResponse.java index 6cce2c4..92ea5f8 100644 --- a/src/jalview/fts/core/FTSRestResponse.java +++ b/src/jalview/fts/core/FTSRestResponse.java @@ -25,6 +25,7 @@ import jalview.fts.api.FTSData; import jalview.fts.api.FTSDataColumnI; import java.util.Collection; +import java.util.Map; import javax.swing.JTable; import javax.swing.table.DefaultTableModel; @@ -107,7 +108,8 @@ public class FTSRestResponse { return String.class; } - return cols[columnIndex - colOffset].getDataColumnClass(); + return cols[columnIndex - colOffset].getDataType() + .getDataTypeClass(); } }; @@ -133,7 +135,8 @@ public class FTSRestResponse } public static void configureTableColumn(JTable tbl_summary, - Collection wantedFields) + Collection wantedFields, + Map columnPrefs) { for (FTSDataColumnI wantedField : wantedFields) { @@ -143,12 +146,29 @@ public class FTSRestResponse wantedField.getMinWidth()); tbl_summary.getColumn(wantedField.getName()).setMaxWidth( wantedField.getMaxWidth()); + int prefedWidth = columnPrefs.get(wantedField.getName()) == null ? wantedField + .getPreferredWidth() : columnPrefs.get(wantedField + .getName()); tbl_summary.getColumn(wantedField.getName()).setPreferredWidth( - wantedField.getPreferredWidth()); + prefedWidth); } 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 index 3e4a1cd..30b6417 100644 --- a/src/jalview/fts/core/GFTSPanel.java +++ b/src/jalview/fts/core/GFTSPanel.java @@ -35,12 +35,14 @@ import java.awt.CardLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; 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; @@ -65,6 +67,7 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import javax.swing.event.InternalFrameEvent; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; @@ -82,7 +85,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI protected JInternalFrame mainFrame = new JInternalFrame( getFTSFrameTitle()); - protected IProgressIndicator progressIdicator; + protected IProgressIndicator progressIndicator; protected JComboBox cmb_searchTarget = new JComboBox(); @@ -143,6 +146,8 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI protected HashSet paginatorCart = new HashSet(); + protected static final DecimalFormat totalNumberformatter = new DecimalFormat( + "###,###"); private JTable tbl_summary = new JTable() { private boolean inLayout; @@ -186,6 +191,8 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI && !inLayout) { resizingColumn.setPreferredWidth(resizingColumn.getWidth()); + String colHeader = resizingColumn.getHeaderValue().toString(); + getTempUserPrefs().put(colHeader, resizingColumn.getWidth()); } resizeAndRepaint(); } @@ -225,6 +232,14 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI try { jbInit(); + mainFrame.addFocusListener(new FocusAdapter() + { + @Override + public void focusGained(FocusEvent e) + { + txt_search.requestFocusInWindow(); + } + }); mainFrame.invalidate(); mainFrame.pack(); } catch (Exception e) @@ -240,6 +255,10 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI */ private void jbInit() throws Exception { + Integer width = getTempUserPrefs().get("FTSPanel.width") == null ? 800 + : getTempUserPrefs().get("FTSPanel.width"); + Integer height = getTempUserPrefs().get("FTSPanel.height") == null ? 400 + : getTempUserPrefs().get("FTSPanel.height"); lbl_warning.setVisible(false); lbl_warning.setFont(new java.awt.Font("Verdana", 0, 12)); lbl_loading.setVisible(false); @@ -344,7 +363,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI }); btn_next_page.setEnabled(false); btn_next_page.setToolTipText(MessageManager - .getString("label.next_page_tooltop")); + .getString("label.next_page_tooltip")); 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() @@ -369,7 +388,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI btn_prev_page.setEnabled(false); btn_prev_page.setToolTipText(MessageManager - .getString("label.prev_page_tooltop")); + .getString("label.prev_page_tooltip")); 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() @@ -424,7 +443,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } } }); - scrl_searchResult.setPreferredSize(new Dimension(800, 400)); + scrl_searchResult.setPreferredSize(new Dimension(width, height)); cmb_searchTarget.setFont(new java.awt.Font("Verdana", 0, 12)); cmb_searchTarget.addActionListener(new ActionListener() @@ -544,7 +563,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI txt_search.setEnabled(false); cmb_searchTarget.setEnabled(false); previousWantedFields = getFTSRestClient() - .getAllDefaulDisplayedDataColumns() + .getAllDefaultDisplayedFTSDataColumns() .toArray(new Object[0]); } if (sourceTabbedPane.getTitleAt(index).equals(searchTabTitle)) @@ -567,7 +586,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } }; tabbedPane.addChangeListener(changeListener); - tabbedPane.setPreferredSize(new Dimension(800, 400)); + tabbedPane.setPreferredSize(new Dimension(width, height)); tabbedPane.add(searchTabTitle, scrl_searchResult); tabbedPane.add(configureCols, new FTSDataColumnPreferences( PreferenceSource.SEARCH_SUMMARY, getFTSRestClient())); @@ -592,9 +611,40 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI mainFrame.setVisible(true); mainFrame.setContentPane(this); mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - Desktop.addInternalFrame(mainFrame, getFTSFrameTitle(), 900, 500); + mainFrame + .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter() + { + @Override + public void internalFrameClosing(InternalFrameEvent e) + { + closeAction(); + } + }); + mainFrame.setVisible(true); + mainFrame.setContentPane(this); + mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + Integer x = getTempUserPrefs().get("FTSPanel.x"); + Integer y = getTempUserPrefs().get("FTSPanel.y"); + if (x != null && y != null) + { + mainFrame.setLocation(x, y); + } + Desktop.addInternalFrame(mainFrame, getFTSFrameTitle(), width, height); } + protected void closeAction() + { + // System.out.println(">>>>>>>>>> closing internal frame!!!"); + // System.out.println("width : " + this.getWidth()); + // System.out.println("heigh : " + this.getHeight()); + // System.out.println("x : " + mainFrame.getX()); + // System.out.println("y : " + mainFrame.getY()); + getTempUserPrefs().put("FTSPanel.width", this.getWidth()); + getTempUserPrefs().put("FTSPanel.height", pnl_results.getHeight()); + getTempUserPrefs().put("FTSPanel.x", mainFrame.getX()); + getTempUserPrefs().put("FTSPanel.y", mainFrame.getY()); + mainFrame.dispose(); + } public class DeferredTextInputListener implements DocumentListener { private final Timer swingTimer; @@ -644,7 +694,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI } return Arrays.equals(getFTSRestClient() - .getAllDefaulDisplayedDataColumns() + .getAllDefaultDisplayedFTSDataColumns() .toArray(new Object[0]), previousWantedFields) ? false : true; @@ -715,8 +765,8 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI protected void btn_back_ActionPerformed() { - mainFrame.dispose(); - new SequenceFetcher(progressIdicator); + closeAction(); + new SequenceFetcher(progressIndicator); } protected void disableActionButtons() @@ -728,7 +778,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI protected void btn_cancel_ActionPerformed() { - mainFrame.dispose(); + closeAction(); } /** diff --git a/src/jalview/fts/service/pdb/PDBFTSPanel.java b/src/jalview/fts/service/pdb/PDBFTSPanel.java index 74b7853..3fe8603 100644 --- a/src/jalview/fts/service/pdb/PDBFTSPanel.java +++ b/src/jalview/fts/service/pdb/PDBFTSPanel.java @@ -29,7 +29,9 @@ import jalview.fts.core.GFTSPanel; import jalview.gui.SequenceFetcher; import jalview.util.MessageManager; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; @SuppressWarnings("serial") public class PDBFTSPanel extends GFTSPanel @@ -39,11 +41,14 @@ public class PDBFTSPanel extends GFTSPanel private String ftsFrameTitle = defaultFTSFrameTitle; + private static Map tempUserPrefs = new HashMap(); + public PDBFTSPanel(SequenceFetcher seqFetcher) { + super(); pageLimit = PDBFTSRestClient.getInstance().getDefaultResponsePageSize(); this.seqFetcher = seqFetcher; - this.progressIdicator = (seqFetcher == null) ? null : seqFetcher + this.progressIndicator = (seqFetcher == null) ? null : seqFetcher .getProgressIndicator(); } @@ -71,7 +76,7 @@ public class PDBFTSPanel extends GFTSPanel String searchTarget = ((FTSDataColumnI) cmb_searchTarget .getSelectedItem()).getCode(); wantedFields = PDBFTSRestClient.getInstance() - .getAllDefaulDisplayedDataColumns(); + .getAllDefaultDisplayedFTSDataColumns(); String searchTerm = decodeSearchTerm(txt_search.getText(), searchTarget); @@ -101,7 +106,7 @@ public class PDBFTSPanel extends GFTSPanel FTSRestResponse.getTableModel(request, resultList.getSearchSummary())); FTSRestResponse.configureTableColumn(getResultTable(), - wantedFields); + wantedFields, tempUserPrefs); getResultTable().setVisible(true); } @@ -116,9 +121,14 @@ public class PDBFTSPanel extends GFTSPanel if (isPaginationEnabled() && resultSetCount > 0) { updateSearchFrameTitle(defaultFTSFrameTitle + " - " + result - + " " + (offSet + 1) + " to " - + (offSet + resultSetCount) + " of " - + totalResultSetCount + + " " + + totalNumberformatter.format((Number) (offSet + 1)) + + " to " + + totalNumberformatter + .format((Number) (offSet + resultSetCount)) + + " of " + + totalNumberformatter + .format((Number) totalResultSetCount) + " " + " (" + (endTime - startTime) + " milli secs)"); } else @@ -262,4 +272,10 @@ public class PDBFTSPanel extends GFTSPanel return true; } + @Override + public Map getTempUserPrefs() + { + return tempUserPrefs; + } + } diff --git a/src/jalview/fts/service/pdb/PDBFTSRestClient.java b/src/jalview/fts/service/pdb/PDBFTSRestClient.java index 29450e8..219d6d6 100644 --- a/src/jalview/fts/service/pdb/PDBFTSRestClient.java +++ b/src/jalview/fts/service/pdb/PDBFTSRestClient.java @@ -326,11 +326,13 @@ public class PDBFTSRestClient extends FTSRestClient { try { - summaryRowData[colCounter++] = (field.getDataColumnClass() == Integer.class) ? Integer + summaryRowData[colCounter++] = (field.getDataType() + .getDataTypeClass() == Integer.class) ? Integer .valueOf(fieldData) - : (field.getDataColumnClass() == Double.class) ? Double + : (field.getDataType() + .getDataTypeClass() == Double.class) ? Double .valueOf(fieldData) - : fieldData; + : sanitiseData(fieldData); } catch (Exception e) { e.printStackTrace(); @@ -389,6 +391,14 @@ public class PDBFTSRestClient extends FTSRestClient }; } + private static String sanitiseData(String data) + { + String cleanData = data.replaceAll("\\[\"", "").replaceAll("\\]\"", "") + .replaceAll("\\[", "").replaceAll("\\]", "") + .replaceAll("\",\"", ", ").replaceAll("\"", ""); + return cleanData; + } + @Override public String getColumnDataConfigFileName() { @@ -404,4 +414,18 @@ public class PDBFTSRestClient extends FTSRestClient } return instance; } + + private Collection allDefaultDisplayedStructureDataColumns; + + public Collection getAllDefaultDisplayedStructureDataColumns() + { + if (allDefaultDisplayedStructureDataColumns == null + || allDefaultDisplayedStructureDataColumns.isEmpty()) + { + allDefaultDisplayedStructureDataColumns = new ArrayList(); + allDefaultDisplayedStructureDataColumns.addAll(super + .getAllDefaultDisplayedFTSDataColumns()); + } + return allDefaultDisplayedStructureDataColumns; + } } diff --git a/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java b/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java index 0447715..5ee518b 100644 --- a/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java +++ b/src/jalview/fts/service/uniprot/UniProtFTSRestClient.java @@ -63,19 +63,20 @@ public class UniProtFTSRestClient extends FTSRestClient : 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(); + } - String query = uniportRestRequest.getFieldToSearchBy() - .equalsIgnoreCase("Search All") ? uniportRestRequest - .getSearchTerm() : uniportRestRequest.getFieldToSearchBy() - + ":" + uniportRestRequest.getSearchTerm(); - - // + (uniportRestRequest.isAllowUnpublishedEntries() ? "" - // : " AND status:REL"); - // System.out.println(">>>>> Query : " + query); - // System.out.println(">>>>> Columns : " + wantedFields); - // System.out.println(">>>>> Response size: " + responseSize - // + " offset : " - // + offSet); WebResource webResource = null; webResource = client.resource(UNIPROT_SEARCH_ENDPOINT) .queryParam("format", "tab") @@ -128,6 +129,17 @@ public class UniProtFTSRestClient extends FTSRestClient } } + 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, @@ -226,9 +238,11 @@ public class UniProtFTSRestClient extends FTSRestClient { try { - summaryRowData[colCounter++] = (field.getDataColumnClass() == Integer.class) ? Integer - .valueOf(fieldData) - : (field.getDataColumnClass() == Double.class) ? Double + summaryRowData[colCounter++] = (field.getDataType() + .getDataTypeClass() == Integer.class) ? Integer + .valueOf(fieldData.replace(",", "")) + : (field.getDataType() + .getDataTypeClass() == Double.class) ? Double .valueOf(fieldData) : fieldData; } catch (Exception e) { diff --git a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java index 6c28ee5..0d02cc0 100644 --- a/src/jalview/fts/service/uniprot/UniprotFTSPanel.java +++ b/src/jalview/fts/service/uniprot/UniprotFTSPanel.java @@ -29,7 +29,9 @@ import jalview.fts.core.GFTSPanel; import jalview.gui.SequenceFetcher; import jalview.util.MessageManager; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; @SuppressWarnings("serial") public class UniprotFTSPanel extends GFTSPanel @@ -40,14 +42,15 @@ public class UniprotFTSPanel extends GFTSPanel private String ftsFrameTitle = defaultFTSFrameTitle; - + private static Map tempUserPrefs = new HashMap(); public UniprotFTSPanel(SequenceFetcher seqFetcher) { + super(); pageLimit = UniProtFTSRestClient.getInstance() .getDefaultResponsePageSize(); this.seqFetcher = seqFetcher; - this.progressIdicator = (seqFetcher == null) ? null : seqFetcher + this.progressIndicator = (seqFetcher == null) ? null : seqFetcher .getProgressIndicator(); } @@ -74,7 +77,7 @@ public class UniprotFTSPanel extends GFTSPanel .getSelectedItem()).getAltCode(); wantedFields = UniProtFTSRestClient.getInstance() - .getAllDefaulDisplayedDataColumns(); + .getAllDefaultDisplayedFTSDataColumns(); String searchTerm = decodeSearchTerm(txt_search.getText(), searchTarget); @@ -104,7 +107,7 @@ public class UniprotFTSPanel extends GFTSPanel FTSRestResponse.getTableModel(request, resultList.getSearchSummary())); FTSRestResponse.configureTableColumn(getResultTable(), - wantedFields); + wantedFields, tempUserPrefs); getResultTable().setVisible(true); } @@ -118,9 +121,14 @@ public class UniprotFTSPanel extends GFTSPanel if (isPaginationEnabled() && resultSetCount > 0) { updateSearchFrameTitle(defaultFTSFrameTitle + " - " + result - + " " + (offSet + 1) + " to " - + (offSet + resultSetCount) + " of " - + totalResultSetCount + + " " + + totalNumberformatter.format((Number) (offSet + 1)) + + " to " + + totalNumberformatter + .format((Number) (offSet + resultSetCount)) + + " of " + + totalNumberformatter + .format((Number) totalResultSetCount) + " " + " (" + (endTime - startTime) + " milli secs)"); } else @@ -220,4 +228,10 @@ public class UniprotFTSPanel extends GFTSPanel return ftsFrameTitle; } + @Override + public Map getTempUserPrefs() + { + return tempUserPrefs; + } + } diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index 7e7bdd3..49bd138 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -32,7 +32,6 @@ import jalview.api.AlignViewControllerI; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.FeatureSettingsControllerI; -import jalview.api.FeatureSettingsModelI; import jalview.api.SplitContainerI; import jalview.api.ViewStyleI; import jalview.api.analysis.ScoreModelI; @@ -54,7 +53,6 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentOrder; import jalview.datamodel.AlignmentView; import jalview.datamodel.ColumnSelection; -import jalview.datamodel.DBRefSource; import jalview.datamodel.HiddenSequences; import jalview.datamodel.PDBEntry; import jalview.datamodel.SeqCigar; @@ -74,7 +72,6 @@ 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; @@ -94,12 +91,10 @@ import jalview.schemes.TaylorColourScheme; import jalview.schemes.TurnColourScheme; import jalview.schemes.UserColourScheme; import jalview.schemes.ZappoColourScheme; -import jalview.structure.StructureSelectionManager; import jalview.util.MessageManager; import jalview.viewmodel.AlignmentViewport; import jalview.ws.DBRefFetcher; import jalview.ws.DBRefFetcher.FetchFinishedListenerI; -import jalview.ws.SequenceFetcher; import jalview.ws.jws1.Discoverer; import jalview.ws.jws2.Jws2Discoverer; import jalview.ws.jws2.jabaws2.Jws2Instance; @@ -113,7 +108,6 @@ 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; @@ -474,7 +468,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void focusGained(FocusEvent e) { - Desktop.setCurrentAlignFrame(AlignFrame.this); + Jalview.setCurrentAlignFrame(AlignFrame.this); } }); @@ -850,8 +844,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, showGroupConservation.setEnabled(!nucleotide); rnahelicesColour.setEnabled(nucleotide); purinePyrimidineColour.setEnabled(nucleotide); - showComplementMenuItem.setText(MessageManager - .getString(nucleotide ? "label.protein" : "label.nucleotide")); + showComplementMenuItem.setText(nucleotide ? MessageManager + .getString("label.protein") : MessageManager + .getString("label.nucleotide")); setColourSelected(jalview.bin.Cache.getDefault( nucleotide ? Preferences.DEFAULT_COLOUR_NUC : Preferences.DEFAULT_COLOUR_PROT, "None")); @@ -1314,7 +1309,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, if (viewport.hasHiddenColumns() && !settings.isExportHiddenColumns()) { - omitHidden = viewport.getViewAsString(false); + omitHidden = viewport.getViewAsString(false, + settings.isExportHiddenSequences()); } int[] alignmentStartEnd = new int[2]; @@ -1325,17 +1321,15 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, else { alignmentToExport = viewport.getAlignment(); - alignmentStartEnd = viewport.getAlignment() - .getVisibleStartAndEndIndex( - viewport - .getColumnSelection().getHiddenColumns()); } + alignmentStartEnd = alignmentToExport + .getVisibleStartAndEndIndex(viewport.getColumnSelection() + .getHiddenColumns()); AlignmentExportData ed = new AlignmentExportData(alignmentToExport, omitHidden, alignmentStartEnd, settings); return ed; } - /** * DOCUMENT ME! * @@ -2436,7 +2430,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, sg.setEndRes(viewport.getAlignment().getWidth() - 1); viewport.setSelectionGroup(sg); viewport.sendSelection(); - alignPanel.paintAlignment(true); + // JAL-2034 - should delegate to + // alignPanel to decide if overview needs + // updating. + alignPanel.paintAlignment(false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); } @@ -2459,7 +2456,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, viewport.setSelectionGroup(null); alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(null); alignPanel.getIdPanel().getIdCanvas().searchResults = null; - alignPanel.paintAlignment(true); + // JAL-2034 - should delegate to + // alignPanel to decide if overview needs + // updating. + alignPanel.paintAlignment(false); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); viewport.sendSelection(); } @@ -2486,6 +2486,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, { sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false); } + // JAL-2034 - should delegate to + // alignPanel to decide if overview needs + // updating. alignPanel.paintAlignment(true); PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId()); @@ -2833,7 +2836,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void expandViews_actionPerformed(ActionEvent e) { - Desktop.instance.explodeViews(this); + Desktop.explodeViews(this); } /** @@ -2974,9 +2977,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // Hide everything by the current selection - this is a hack - we do the // invert and then hide // first check that there will be visible columns after the invert. - if ((viewport.getColumnSelection() != null - && viewport.getColumnSelection().getSelected() != null && viewport - .getColumnSelection().getSelected().size() > 0) + if (viewport.hasSelectedColumns() || (sg != null && sg.getSize() > 0 && sg.getStartRes() <= sg .getEndRes())) { @@ -3004,8 +3005,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, hideSelSequences_actionPerformed(null); hide = true; } - else if (!(toggleCols && viewport.getColumnSelection().getSelected() - .size() > 0)) + else if (!(toggleCols && viewport.hasSelectedColumns())) { showAllSeqs_actionPerformed(null); } @@ -3013,7 +3013,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, if (toggleCols) { - if (viewport.getColumnSelection().getSelected().size() > 0) + if (viewport.hasSelectedColumns()) { hideSelColumns_actionPerformed(null); if (!toggleSeqs) @@ -3212,30 +3212,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } /** - * Set or clear 'Show Sequence Features' - * - * @param evt - * DOCUMENT ME! - */ - @Override - public void showSeqFeaturesHeight_actionPerformed(ActionEvent evt) - { - viewport.setShowSequenceFeaturesHeight(showSeqFeaturesHeight - .isSelected()); - if (viewport.isShowSequenceFeaturesHeight()) - { - // ensure we're actually displaying features - viewport.setShowSequenceFeatures(true); - showSeqFeatures.setSelected(true); - } - alignPanel.paintAlignment(true); - if (alignPanel.getOverviewPanel() != null) - { - alignPanel.getOverviewPanel().updateOverviewImage(); - } - } - - /** * Action on toggle of the 'Show annotations' menu item. This shows or hides * the annotations panel as a whole. * @@ -3641,34 +3617,51 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void mousePressed(MouseEvent evt) { - if (evt.isPopupTrigger()) + if (evt.isPopupTrigger()) // Mac { - radioItem.removeActionListener(radioItem.getActionListeners()[0]); + offerRemoval(radioItem); + } + } - int option = JOptionPane.showInternalConfirmDialog( - jalview.gui.Desktop.desktop, - MessageManager - .getString("label.remove_from_default_list"), - MessageManager - .getString("label.remove_user_defined_colour"), - JOptionPane.YES_NO_OPTION); - if (option == JOptionPane.YES_OPTION) - { - jalview.gui.UserDefinedColours - .removeColourFromDefaults(radioItem.getText()); - colourMenu.remove(radioItem); - } - else + @Override + public void mouseReleased(MouseEvent evt) + { + if (evt.isPopupTrigger()) // Windows + { + offerRemoval(radioItem); + } + } + + /** + * @param radioItem + */ + void offerRemoval(final JRadioButtonMenuItem radioItem) + { + radioItem.removeActionListener(radioItem.getActionListeners()[0]); + + int option = JOptionPane.showInternalConfirmDialog( + jalview.gui.Desktop.desktop, + MessageManager + .getString("label.remove_from_default_list"), + MessageManager + .getString("label.remove_user_defined_colour"), + JOptionPane.YES_NO_OPTION); + if (option == JOptionPane.YES_OPTION) + { + jalview.gui.UserDefinedColours + .removeColourFromDefaults(radioItem.getText()); + colourMenu.remove(radioItem); + } + else + { + radioItem.addActionListener(new ActionListener() { - radioItem.addActionListener(new ActionListener() + @Override + public void actionPerformed(ActionEvent evt) { - @Override - public void actionPerformed(ActionEvent evt) - { - userDefinedColour_actionPerformed(evt); - } - }); - } + userDefinedColour_actionPerformed(evt); + } + }); } } }); @@ -4460,22 +4453,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, // object broker mechanism. final Vector wsmenu = new Vector(); final IProgressIndicator af = me; + + /* + * do not i18n these strings - they are hard-coded in class + * compbio.data.msa.Category, Jws2Discoverer.isRecalculable() and + * SequenceAnnotationWSClient.initSequenceAnnotationWSClient() + */ final JMenu msawsmenu = new JMenu("Alignment"); final JMenu secstrmenu = new JMenu( "Secondary Structure Prediction"); final JMenu seqsrchmenu = new JMenu("Sequence Database Search"); final JMenu analymenu = new JMenu("Analysis"); final JMenu dismenu = new JMenu("Protein Disorder"); - // final JMenu msawsmenu = new - // JMenu(MessageManager.getString("label.alignment")); - // final JMenu secstrmenu = new - // JMenu(MessageManager.getString("label.secondary_structure_prediction")); - // final JMenu seqsrchmenu = new - // JMenu(MessageManager.getString("label.sequence_database_search")); - // final JMenu analymenu = new - // JMenu(MessageManager.getString("label.analysis")); - // final JMenu dismenu = new - // JMenu(MessageManager.getString("label.protein_disorder")); // JAL-940 - only show secondary structure prediction services from // the legacy server if (// Cache.getDefault("SHOW_JWS1_SERVICES", true) @@ -4635,38 +4624,45 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } /** - * Searches selected sequences for xRef products and builds the Show - * Cross-References menu (formerly called Show Products) + * Searches the alignment sequences for xRefs and builds the Show + * Cross-References menu (formerly called Show Products), with database + * sources for which cross-references are found (protein sources for a + * nucleotide alignment and vice versa) * - * @return true if Show Cross-references menu should be enabled. + * @return true if Show Cross-references menu should be enabled */ public boolean canShowProducts() { - SequenceI[] selection = viewport.getSequenceSelection(); + SequenceI[] seqs = viewport.getAlignment().getSequencesArray(); AlignmentI dataset = viewport.getAlignment().getDataset(); + + showProducts.removeAll(); + final boolean dna = viewport.getAlignment().isNucleotide(); + + if (seqs == null || seqs.length == 0) + { + // nothing to see here. + return false; + } + boolean showp = false; try { - showProducts.removeAll(); - final boolean dna = viewport.getAlignment().isNucleotide(); - String[] ptypes = (selection == null || selection.length == 0) ? null - : CrossRef.findSequenceXrefTypes(dna, selection, dataset); + List ptypes = new CrossRef(seqs, dataset) + .findXrefSourcesForSequences(dna); - for (int t = 0; ptypes != null && t < ptypes.length; t++) + for (final String source : ptypes) { showp = true; final AlignFrame af = this; - final String source = ptypes[t]; - JMenuItem xtype = new JMenuItem(ptypes[t]); + JMenuItem xtype = new JMenuItem(source); xtype.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { showProductsFor(af.viewport.getSequenceSelection(), dna, source); } - }); showProducts.add(xtype); } @@ -4674,7 +4670,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, showProducts.setEnabled(showp); } catch (Exception e) { - jalview.bin.Cache.log + Cache.log .warn("canShowProducts threw an exception - please report to help@jalview.org", e); return false; @@ -4682,232 +4678,22 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, return showp; } - protected void showProductsFor(final SequenceI[] sel, final boolean dna, - final String source) + /** + * Finds and displays cross-references for the selected sequences (protein + * products for nucleotide sequences, dna coding sequences for peptides). + * + * @param sel + * the sequences to show cross-references for + * @param dna + * true if from a nucleotide alignment (so showing proteins) + * @param source + * the database to show cross-references for + */ + protected void showProductsFor(final SequenceI[] sel, + final boolean _odna, final String source) { - Runnable foo = new Runnable() - { - - @Override - public void run() - { - final long sttime = System.currentTimeMillis(); - AlignFrame.this.setProgressBar(MessageManager.formatMessage( - "status.searching_for_sequences_from", - new Object[] { source }), sttime); - try - { - AlignmentI alignment = AlignFrame.this.getViewport() - .getAlignment(); - AlignmentI xrefs = CrossRef.findXrefSequences(sel, dna, source, - alignment); - if (xrefs != null) - { - /* - * get display scheme (if any) to apply to features - */ - FeatureSettingsModelI featureColourScheme = new SequenceFetcher() - .getFeatureColourScheme(source); - - AlignmentI al = makeCrossReferencesAlignment( - alignment.getDataset(), xrefs); - - 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 - .getString("label.for"), getTitle()); - newFrame.setTitle(newtitle); - - if (!Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true)) - { - /* - * split frame display is turned off in preferences file - */ - Desktop.addInternalFrame(newFrame, newtitle, DEFAULT_WIDTH, - DEFAULT_HEIGHT); - return; // via finally clause - } - - /* - * Make a copy of this alignment (sharing the same dataset - * sequences). If we are DNA, drop introns and update mappings - */ - AlignmentI copyAlignment = null; - final SequenceI[] sequenceSelection = AlignFrame.this.viewport - .getSequenceSelection(); - List cf = xrefs.getCodonFrames(); - boolean copyAlignmentIsAligned = false; - if (dna) - { - copyAlignment = AlignmentUtils.makeCdsAlignment( - sequenceSelection, cf, alignment); - if (copyAlignment.getHeight() == 0) - { - System.err.println("Failed to make CDS alignment"); - } - al.getCodonFrames().clear(); - al.getCodonFrames().addAll(copyAlignment.getCodonFrames()); - - /* - * pending getting Embl transcripts to 'align', - * we are only doing this for Ensembl - */ - // TODO proper criteria for 'can align as cdna' - if (DBRefSource.ENSEMBL.equalsIgnoreCase(source) - || AlignmentUtils.looksLikeEnsembl(alignment)) - { - copyAlignment.alignAs(alignment); - copyAlignmentIsAligned = true; - } - } - else - { - copyAlignment = AlignmentUtils.makeCopyAlignment( - sequenceSelection, xrefs.getSequencesArray()); - copyAlignment.getCodonFrames().addAll(cf); - } - copyAlignment.setGapCharacter(AlignFrame.this.viewport - .getGapCharacter()); - - StructureSelectionManager ssm = StructureSelectionManager - .getStructureSelectionManager(Desktop.instance); - ssm.registerMappings(cf); - - if (copyAlignment.getHeight() <= 0) - { - System.err.println("No Sequences generated for xRef type " - + source); - return; - } - /* - * align protein to dna - */ - if (dna && copyAlignmentIsAligned) - { - al.alignAs(copyAlignment); - } - else - { - /* - * align cdna to protein - currently only if - * fetching and aligning Ensembl transcripts! - */ - if (DBRefSource.ENSEMBL.equalsIgnoreCase(source)) - { - copyAlignment.alignAs(al); - } - } - - AlignFrame copyThis = new AlignFrame(copyAlignment, - AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); - copyThis.setTitle(AlignFrame.this.getTitle()); - - boolean showSequenceFeatures = viewport - .isShowSequenceFeatures(); - newFrame.setShowSeqFeatures(showSequenceFeatures); - copyThis.setShowSeqFeatures(showSequenceFeatures); - FeatureRenderer myFeatureStyling = alignPanel.getSeqPanel().seqCanvas - .getFeatureRenderer(); - - /* - * copy feature rendering settings to split frame - */ - newFrame.alignPanel.getSeqPanel().seqCanvas - .getFeatureRenderer() - .transferSettings(myFeatureStyling); - copyThis.alignPanel.getSeqPanel().seqCanvas - .getFeatureRenderer() - .transferSettings(myFeatureStyling); - - /* - * apply 'database source' feature configuration - * if any was found - */ - // TODO is this the feature colouring for the original - // alignment or the fetched xrefs? either could be Ensembl - newFrame.getViewport().applyFeaturesStyle(featureColourScheme); - copyThis.getViewport().applyFeaturesStyle(featureColourScheme); - - SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame, - dna ? newFrame : copyThis); - newFrame.setVisible(true); - copyThis.setVisible(true); - String linkedTitle = MessageManager - .getString("label.linked_view_title"); - Desktop.addInternalFrame(sf, linkedTitle, -1, -1); - sf.adjustDivider(); - } - } catch (Exception 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); - } finally - { - AlignFrame.this.setProgressBar(MessageManager.formatMessage( - "status.finished_searching_for_sequences_from", - new Object[] { source }), sttime); - } - } - - /** - * Makes an alignment containing the given sequences. If this is of the - * same type as the given dataset (nucleotide/protein), then the new - * alignment shares the same dataset, and its dataset sequences are added - * to it. Otherwise a new dataset sequence is created for the - * cross-references. - * - * @param dataset - * @param seqs - * @return - */ - protected AlignmentI makeCrossReferencesAlignment(AlignmentI dataset, - AlignmentI seqs) - { - boolean sameType = dataset.isNucleotide() == seqs.isNucleotide(); - - SequenceI[] sprods = new SequenceI[seqs.getHeight()]; - for (int s = 0; s < sprods.length; s++) - { - sprods[s] = (seqs.getSequenceAt(s)).deriveSequence(); - if (sameType) - { - if (dataset.getSequences() == null - || !dataset.getSequences().contains( - sprods[s].getDatasetSequence())) - { - dataset.addSequence(sprods[s].getDatasetSequence()); - } - } - sprods[s].updatePDBIds(); - } - Alignment al = new Alignment(sprods); - if (sameType) - { - al.setDataset((Alignment) dataset); - } - else - { - al.createDatasetAlignment(); - } - return al; - } - - }; - Thread frunner = new Thread(foo); - frunner.start(); + new Thread(CrossRefAction.showProductsFor(sel, _odna, source, this)) + .start(); } /** @@ -4931,7 +4717,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, .getString("label.error_when_translating_sequences_submit_bug_report"); final String errorTitle = MessageManager .getString("label.implementation_error") - + MessageManager.getString("translation_failed"); + + MessageManager.getString("label.translation_failed"); JOptionPane.showMessageDialog(Desktop.desktop, msg, errorTitle, JOptionPane.ERROR_MESSAGE); return; @@ -5030,49 +4816,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 files = new ArrayList(), protocols = new ArrayList(); 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(); @@ -5547,8 +5295,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, AlignFrame.this.setMenusForViewport(); } }); - dbRefFetcher - .fetchDBRefs(false); + dbRefFetcher.fetchDBRefs(false); } }).start(); @@ -5989,8 +5736,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, protected void setAnnotationsVisibility(boolean visible, boolean forSequences, boolean forAlignment) { - for (AlignmentAnnotation aa : alignPanel.getAlignment() - .getAlignmentAnnotation()) + AlignmentAnnotation[] anns = alignPanel.getAlignment() + .getAlignmentAnnotation(); + if (anns == null) + { + return; + } + for (AlignmentAnnotation aa : anns) { /* * don't display non-positional annotations on an alignment @@ -6106,9 +5858,12 @@ 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()); @@ -6124,7 +5879,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override protected void runGroovy_actionPerformed() { - Desktop.setCurrentAlignFrame(this); + Jalview.setCurrentAlignFrame(this); groovy.ui.Console console = Desktop.getGroovyConsole(); if (console != null) { diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 692cd18..62e05d0 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -507,17 +507,6 @@ public class AlignViewport extends AlignmentViewport implements /** * DOCUMENT ME! * - * @return DOCUMENT ME! - */ - @Override - public ColumnSelection getColumnSelection() - { - return colSel; - } - - /** - * DOCUMENT ME! - * * @param tree * DOCUMENT ME! */ @@ -683,27 +672,42 @@ public class AlignViewport extends AlignmentViewport implements List seqvectors = new ArrayList(); for (PDBEntry pdb : pdbEntries) { - List seqs = new ArrayList(); + List choosenSeqs = new ArrayList(); for (SequenceI sq : alignment.getSequences()) { - Vector pdbs = sq.getDatasetSequence().getAllPDBEntries(); - if (pdbs == null) + Vector pdbRefEntries = sq.getDatasetSequence().getAllPDBEntries(); + if (pdbRefEntries == null) { continue; } - for (PDBEntry p1 : pdbs) + for (PDBEntry pdbRefEntry : pdbRefEntries) { - if (p1.getId().equals(pdb.getId())) + if (pdbRefEntry.getId().equals(pdb.getId())) { - if (!seqs.contains(sq)) + if (pdbRefEntry.getChainCode() != null + && pdb.getChainCode() != null) + { + if (pdbRefEntry.getChainCode().equalsIgnoreCase( + pdb.getChainCode()) + && !choosenSeqs.contains(sq)) + { + choosenSeqs.add(sq); + continue; + } + } + else { - seqs.add(sq); - continue; + if (!choosenSeqs.contains(sq)) + { + choosenSeqs.add(sq); + continue; + } } + } } } - seqvectors.add(seqs.toArray(new SequenceI[seqs.size()])); + seqvectors.add(choosenSeqs.toArray(new SequenceI[choosenSeqs.size()])); } return seqvectors.toArray(new SequenceI[seqvectors.size()][]); } @@ -1103,6 +1107,7 @@ public class AlignViewport extends AlignmentViewport implements * * @param featureSettings */ + @Override public void applyFeaturesStyle(FeatureSettingsModelI featureSettings) { if (featureSettings == null) diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index 7b0d717..c259ef3 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -1270,6 +1270,7 @@ public class AlignmentPanel extends GAlignmentPanel implements void makeAlignmentImage(jalview.util.ImageMaker.TYPE type, File file) { + int boarderBottomOffset = 5; long pSessionId = System.currentTimeMillis(); headless = (System.getProperty("java.awt.headless") != null && System .getProperty("java.awt.headless").equals("true")); @@ -1306,14 +1307,15 @@ public class AlignmentPanel extends GAlignmentPanel implements } im = new jalview.util.ImageMaker(this, type, imageAction, - aDimension.getWidth(), aDimension.getHeight(), file, + aDimension.getWidth(), aDimension.getHeight() + + boarderBottomOffset, file, imageTitle, alignFrame, pSessionId, headless); if (av.getWrapAlignment()) { if (im.getGraphics() != null) { printWrappedAlignment(im.getGraphics(), aDimension.getWidth(), - aDimension.getHeight(), 0); + aDimension.getHeight() + boarderBottomOffset, 0); im.writeImage(); } } diff --git a/src/jalview/gui/AnnotationColourChooser.java b/src/jalview/gui/AnnotationColourChooser.java index 19eed27..93c9a6b 100644 --- a/src/jalview/gui/AnnotationColourChooser.java +++ b/src/jalview/gui/AnnotationColourChooser.java @@ -141,7 +141,7 @@ public class AnnotationColourChooser extends AnnotationRowFilter default: throw new Error( MessageManager - .getString("error.implementation_error_dont_know_about_thereshold_setting")); + .getString("error.implementation_error_dont_know_about_threshold_setting")); } thresholdIsMin.setSelected(acg.thresholdIsMinMax); thresholdValue.setText("" + acg.getAnnotationThreshold()); diff --git a/src/jalview/gui/AnnotationExporter.java b/src/jalview/gui/AnnotationExporter.java index cffc52f..469e495 100644 --- a/src/jalview/gui/AnnotationExporter.java +++ b/src/jalview/gui/AnnotationExporter.java @@ -156,6 +156,8 @@ public class AnnotationExporter extends JPanel .getString("label.no_features_on_alignment"); if (features) { + Map displayedFeatureColours = ap + .getFeatureRenderer().getDisplayedFeatureCols(); FeaturesFile formatter = new FeaturesFile(); SequenceI[] sequences = ap.av.getAlignment().getSequencesArray(); Map featureColours = ap.getFeatureRenderer() @@ -163,11 +165,16 @@ public class AnnotationExporter extends JPanel boolean includeNonPositional = ap.av.isShowNPFeats(); if (GFFFormat.isSelected()) { + text = new FeaturesFile().printGffFormat(ap.av.getAlignment() + .getDataset().getSequencesArray(), displayedFeatureColours, + true, ap.av.isShowNPFeats()); text = formatter.printGffFormat(sequences, featureColours, true, includeNonPositional); } else { + text = new FeaturesFile().printJalviewFormat(ap.av.getAlignment() + .getDataset().getSequencesArray(), displayedFeatureColours, true, ap.av.isShowNPFeats()); // ap.av.featuresDisplayed); text = formatter.printJalviewFormat(sequences, featureColours, true, includeNonPositional); } diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index 68df498..f995c60 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -219,6 +219,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, AlignmentAnnotation[] aa = ap.av.getAlignment() .getAlignmentAnnotation(); + boolean fullRepaint = false; if (evt.getActionCommand().equals(ADDNEW)) { AlignmentAnnotation newAnnotation = new AlignmentAnnotation(null, @@ -231,11 +232,16 @@ public class AnnotationLabels extends JPanel implements MouseListener, ap.av.getAlignment().addAnnotation(newAnnotation); ap.av.getAlignment().setAnnotationIndex(newAnnotation, 0); + fullRepaint = true; } else if (evt.getActionCommand().equals(EDITNAME)) { + String name = aa[selectedRow].label; editLabelDescription(aa[selectedRow]); - repaint(); + if (!name.equalsIgnoreCase(aa[selectedRow].label)) + { + fullRepaint = true; + } } else if (evt.getActionCommand().equals(HIDE)) { @@ -244,6 +250,8 @@ public class AnnotationLabels extends JPanel implements MouseListener, else if (evt.getActionCommand().equals(DELETE)) { ap.av.getAlignment().deleteAnnotation(aa[selectedRow]); + ap.av.getCalcManager().removeWorkerForAnnotation(aa[selectedRow]); + fullRepaint = true; } else if (evt.getActionCommand().equals(SHOWALL)) { @@ -254,6 +262,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, aa[i].visible = true; } } + fullRepaint = true; } else if (evt.getActionCommand().equals(OUTPUT_TEXT)) { @@ -282,18 +291,30 @@ public class AnnotationLabels extends JPanel implements MouseListener, aa[selectedRow].scaleColLabel = !aa[selectedRow].scaleColLabel; } - refresh(); + refresh(fullRepaint); } /** * Redraw sensibly. + * + * @adjustHeight if true, try to recalculate panel height for visible + * annotations */ - protected void refresh() + protected void refresh(boolean adjustHeight) { - ap.validateAnnotationDimensions(false); + ap.validateAnnotationDimensions(adjustHeight); ap.addNotify(); - ap.repaint(); + if (adjustHeight) + { + // sort, repaint, update overview + ap.paintAlignment(true); + } + else + { + // lightweight repaint + ap.repaint(); + } } /** @@ -304,6 +325,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, */ boolean editLabelDescription(AlignmentAnnotation annotation) { + // TODO i18n EditNameDialog dialog = new EditNameDialog(annotation.label, annotation.description, " Annotation Name ", "Annotation Description ", "Edit Annotation Name/Description", @@ -331,12 +353,20 @@ public class AnnotationLabels extends JPanel implements MouseListener, { getSelectedRow(evt.getY() - getScrollOffset()); oldY = evt.getY(); - if (!evt.isPopupTrigger()) + if (evt.isPopupTrigger()) { - return; + showPopupMenu(evt); } + } + + /** + * Build and show the Pop-up menu at the right-click mouse position + * + * @param evt + */ + void showPopupMenu(MouseEvent evt) + { evt.consume(); - // handle popup menu event final AlignmentAnnotation[] aa = ap.av.getAlignment() .getAlignmentAnnotation(); @@ -389,7 +419,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, // ann.visible = false; // } // } - refresh(); + refresh(true); } }); pop.add(hideType); @@ -594,7 +624,6 @@ public class AnnotationLabels extends JPanel implements MouseListener, } } pop.show(this, evt.getX(), evt.getY()); - } /** @@ -606,6 +635,12 @@ public class AnnotationLabels extends JPanel implements MouseListener, @Override public void mouseReleased(MouseEvent evt) { + if (evt.isPopupTrigger()) + { + showPopupMenu(evt); + return; + } + int start = selectedRow; getSelectedRow(evt.getY() - getScrollOffset()); int end = selectedRow; @@ -802,8 +837,45 @@ public class AnnotationLabels extends JPanel implements MouseListener, // todo: make the ap scroll to the selection - not necessary, first // click highlights/scrolls, second selects ap.getSeqPanel().ap.getIdPanel().highlightSearchResults(null); - ap.av.setSelectionGroup(// new SequenceGroup( - aa[selectedRow].groupRef); // ); + // process modifiers + SequenceGroup sg = ap.av.getSelectionGroup(); + if (sg == null + || sg == aa[selectedRow].groupRef + || !(jalview.util.Platform.isControlDown(evt) || evt + .isShiftDown())) + { + if (jalview.util.Platform.isControlDown(evt) + || evt.isShiftDown()) + { + // clone a new selection group from the associated group + ap.av.setSelectionGroup(new SequenceGroup( + aa[selectedRow].groupRef)); + } + else + { + // set selection to the associated group so it can be edited + ap.av.setSelectionGroup(aa[selectedRow].groupRef); + } + } + else + { + // modify current selection with associated group + int remainToAdd = aa[selectedRow].groupRef.getSize(); + for (SequenceI sgs : aa[selectedRow].groupRef.getSequences()) + { + if (jalview.util.Platform.isControlDown(evt)) + { + sg.addOrRemove(sgs, --remainToAdd == 0); + } + else + { + // notionally, we should also add intermediate sequences from + // last added sequence ? + sg.addSequence(sgs, --remainToAdd == 0); + } + } + } + ap.paintAlignment(false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); ap.av.sendSelection(); @@ -833,7 +905,8 @@ public class AnnotationLabels extends JPanel implements MouseListener, // we make a copy rather than edit the current selection if no // modifiers pressed // see Enhancement JAL-1557 - if (!(evt.isControlDown() || evt.isShiftDown())) + if (!(jalview.util.Platform.isControlDown(evt) || evt + .isShiftDown())) { sg = new SequenceGroup(sg); sg.clear(); @@ -841,7 +914,7 @@ public class AnnotationLabels extends JPanel implements MouseListener, } else { - if (evt.isControlDown()) + if (jalview.util.Platform.isControlDown(evt)) { sg.addOrRemove(aa[selectedRow].sequenceRef, true); } @@ -861,9 +934,9 @@ public class AnnotationLabels extends JPanel implements MouseListener, sg.addSequence(aa[selectedRow].sequenceRef, false); } ap.av.setSelectionGroup(sg); - ap.av.sendSelection(); ap.paintAlignment(false); PaintRefresher.Refresh(ap, ap.av.getSequenceSetId()); + ap.av.sendSelection(); } } diff --git a/src/jalview/gui/AnnotationPanel.java b/src/jalview/gui/AnnotationPanel.java index bb311ef..7450555 100755 --- a/src/jalview/gui/AnnotationPanel.java +++ b/src/jalview/gui/AnnotationPanel.java @@ -26,6 +26,8 @@ import jalview.datamodel.ColumnSelection; import jalview.datamodel.SequenceI; import jalview.renderer.AnnotationRenderer; import jalview.renderer.AwtRenderPanelI; +import jalview.schemes.ResidueProperties; +import jalview.util.Comparison; import jalview.util.MessageManager; import java.awt.AlphaComposite; @@ -47,6 +49,9 @@ import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import javax.swing.JColorChooser; import javax.swing.JMenuItem; @@ -286,14 +291,18 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, aa[activeRow].annotations = anot; } - if (evt.getActionCommand().equals(REMOVE)) + String action = evt.getActionCommand(); + if (action.equals(REMOVE)) { - for (int sel : av.getColumnSelection().getSelected()) + for (int index : av.getColumnSelection().getSelected()) { - anot[sel] = null; + if (av.getColumnSelection().isVisible(index)) + { + anot[index] = null; + } } } - else if (evt.getActionCommand().equals(LABEL)) + else if (action.equals(LABEL)) { String exMesg = collectAnnotVals(anot, LABEL); String label = JOptionPane.showInputDialog(this, @@ -318,10 +327,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, if (anot[index] == null) { - anot[index] = new Annotation(label, "", ' ', 0); // TODO: verify that - // null exceptions - // aren't raised - // elsewhere. + anot[index] = new Annotation(label, "", ' ', 0); } else { @@ -329,7 +335,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, } } } - else if (evt.getActionCommand().equals(COLOUR)) + else if (action.equals(COLOUR)) { Color col = JColorChooser.showDialog(this, MessageManager.getString("label.select_foreground_colour"), @@ -351,26 +357,27 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, } } else - // HELIX OR SHEET + // HELIX, SHEET or STEM { char type = 0; - String symbol = "\u03B1"; + String symbol = "\u03B1"; // alpha - if (evt.getActionCommand().equals(HELIX)) + if (action.equals(HELIX)) { type = 'H'; } - else if (evt.getActionCommand().equals(SHEET)) + else if (action.equals(SHEET)) { type = 'E'; - symbol = "\u03B2"; + symbol = "\u03B2"; // beta } // Added by LML to color stems - else if (evt.getActionCommand().equals(STEM)) + else if (action.equals(STEM)) { type = 'S'; - symbol = "\u03C3"; + int column = av.getColumnSelection().getSelectedRanges().get(0)[0]; + symbol = aa[activeRow].getDefaultRnaHelixSymbol(column); } if (!aa[activeRow].hasIcons) @@ -389,7 +396,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, if ((label.length() > 0) && !aa[activeRow].hasText) { aa[activeRow].hasText = true; - if (evt.getActionCommand().equals(STEM)) + if (action.equals(STEM)) { aa[activeRow].showAllColLabels = true; } @@ -412,22 +419,41 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, } } + av.getAlignment().validateAnnotation(aa[activeRow]); ap.alignmentChanged(); - + ap.alignFrame.setMenusForViewport(); adjustPanelHeight(); repaint(); return; } - private String collectAnnotVals(Annotation[] anot, String label2) + /** + * Returns any existing annotation concatenated as a string. For each + * annotation, takes the description, if any, else the secondary structure + * character (if type is HELIX, SHEET or STEM), else the display character (if + * type is LABEL). + * + * @param anots + * @param type + * @return + */ + private String collectAnnotVals(Annotation[] anots, String type) { - String collatedInput = ""; + // TODO is this method wanted? why? 'last' is not used + + StringBuilder collatedInput = new StringBuilder(64); String last = ""; ColumnSelection viscols = av.getColumnSelection(); - // TODO: refactor and save av.getColumnSelection for efficiency - for (int index : viscols.getSelected()) + + /* + * the selection list (read-only view) is in selection order, not + * column order; make a copy so we can sort it + */ + List selected = new ArrayList(viscols.getSelected()); + Collections.sort(selected); + for (int index : selected) { // always check for current display state - just in case if (!viscols.isVisible(index)) @@ -435,22 +461,22 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, continue; } String tlabel = null; - if (anot[index] != null) + if (anots[index] != null) { // LML added stem code - if (label2.equals(HELIX) || label2.equals(SHEET) - || label2.equals(STEM) || label2.equals(LABEL)) + if (type.equals(HELIX) || type.equals(SHEET) + || type.equals(STEM) || type.equals(LABEL)) { - tlabel = anot[index].description; + tlabel = anots[index].description; if (tlabel == null || tlabel.length() < 1) { - if (label2.equals(HELIX) || label2.equals(SHEET) - || label2.equals(STEM)) + if (type.equals(HELIX) || type.equals(SHEET) + || type.equals(STEM)) { - tlabel = "" + anot[index].secondaryStructure; + tlabel = "" + anots[index].secondaryStructure; } else { - tlabel = "" + anot[index].displayCharacter; + tlabel = "" + anots[index].displayCharacter; } } } @@ -458,13 +484,13 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, { if (last.length() > 0) { - collatedInput += " "; + collatedInput.append(" "); } - collatedInput += tlabel; + collatedInput.append(tlabel); } } } - return collatedInput; + return collatedInput.toString(); } /** @@ -486,6 +512,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, int height = 0; activeRow = -1; + final int y = evt.getY(); for (int i = 0; i < aa.length; i++) { if (aa[i].visible) @@ -493,7 +520,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, height += aa[i].height; } - if (evt.getY() < height) + if (y < height) { if (aa[i].editable) { @@ -503,62 +530,71 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, { // Stretch Graph graphStretch = i; - graphStretchY = evt.getY(); + graphStretchY = y; } break; } } + /* + * isPopupTrigger fires in mousePressed on Mac, + * not until mouseRelease on Windows + */ if (evt.isPopupTrigger() && activeRow != -1) { - if (av.getColumnSelection() == null) - { - return; - } + showPopupMenu(y, evt.getX()); + return; + } - JPopupMenu pop = new JPopupMenu( - MessageManager.getString("label.structure_type")); - JMenuItem item; - /* - * Just display the needed structure options - */ - if (av.getAlignment().isNucleotide() == true) - { - item = new JMenuItem(STEM); - item.addActionListener(this); - pop.add(item); - } - else - { - item = new JMenuItem(HELIX); - item.addActionListener(this); - pop.add(item); - item = new JMenuItem(SHEET); - item.addActionListener(this); - pop.add(item); - } - item = new JMenuItem(LABEL); + ap.getScalePanel().mousePressed(evt); + } + + /** + * Construct and display a context menu at the right-click position + * + * @param y + * @param x + */ + void showPopupMenu(final int y, int x) + { + if (av.getColumnSelection() == null + || av.getColumnSelection().isEmpty()) + { + return; + } + + JPopupMenu pop = new JPopupMenu( + MessageManager.getString("label.structure_type")); + JMenuItem item; + /* + * Just display the needed structure options + */ + if (av.getAlignment().isNucleotide()) + { + item = new JMenuItem(STEM); item.addActionListener(this); pop.add(item); - item = new JMenuItem(COLOUR); + } + else + { + item = new JMenuItem(HELIX); item.addActionListener(this); pop.add(item); - item = new JMenuItem(REMOVE); + item = new JMenuItem(SHEET); item.addActionListener(this); pop.add(item); - pop.show(this, evt.getX(), evt.getY()); - - return; - } - - if (aa == null) - { - return; } - - ap.getScalePanel().mousePressed(evt); - + item = new JMenuItem(LABEL); + item.addActionListener(this); + pop.add(item); + item = new JMenuItem(COLOUR); + item.addActionListener(this); + pop.add(item); + item = new JMenuItem(REMOVE); + item.addActionListener(this); + pop.add(item); + pop.show(this, x, y); } /** @@ -574,6 +610,16 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, graphStretchY = -1; mouseDragging = false; ap.getScalePanel().mouseReleased(evt); + + /* + * isPopupTrigger is set in mouseReleased on Windows + * (in mousePressed on Mac) + */ + if (evt.isPopupTrigger() && activeRow != -1) + { + showPopupMenu(evt.getY(), evt.getX()); + } + } /** @@ -628,10 +674,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, } /** - * DOCUMENT ME! + * Constructs the tooltip, and constructs and displays a status message, for + * the current mouse position * * @param evt - * DOCUMENT ME! */ @Override public void mouseMoved(MouseEvent evt) @@ -657,7 +703,6 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, if (evt.getY() < height) { row = i; - break; } } @@ -668,64 +713,140 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI, return; } - int res = (evt.getX() / av.getCharWidth()) + av.getStartRes(); + int column = (evt.getX() / av.getCharWidth()) + av.getStartRes(); if (av.hasHiddenColumns()) { - res = av.getColumnSelection().adjustForHiddenColumns(res); + column = av.getColumnSelection().adjustForHiddenColumns(column); + } + + AlignmentAnnotation ann = aa[row]; + if (row > -1 && ann.annotations != null + && column < ann.annotations.length) + { + buildToolTip(ann, column, aa); + setStatusMessage(column, ann); + } + else + { + this.setToolTipText(null); + ap.alignFrame.statusBar.setText(" "); } + } - if (row > -1 && aa[row].annotations != null - && res < aa[row].annotations.length) + /** + * Builds a tooltip for the annotation at the current mouse position. + * + * @param ann + * @param column + * @param anns + */ + void buildToolTip(AlignmentAnnotation ann, int column, + AlignmentAnnotation[] anns) + { + if (ann.graphGroup > -1) { - if (aa[row].graphGroup > -1) + StringBuilder tip = new StringBuilder(32); + tip.append(""); + for (int i = 0; i < anns.length; i++) { - StringBuffer tip = new StringBuffer(""); - for (int gg = 0; gg < aa.length; gg++) + if (anns[i].graphGroup == ann.graphGroup + && anns[i].annotations[column] != null) { - if (aa[gg].graphGroup == aa[row].graphGroup - && aa[gg].annotations[res] != null) + tip.append(anns[i].label); + String description = anns[i].annotations[column].description; + if (description != null && description.length() > 0) { - tip.append(aa[gg].label + " " - + aa[gg].annotations[res].description + "
        "); + tip.append(" ").append(description); } - } - if (tip.length() != 6) - { - tip.setLength(tip.length() - 4); - this.setToolTipText(tip.toString() + ""); + tip.append("
        "); } } - else if (aa[row].annotations[res] != null - && aa[row].annotations[res].description != null - && aa[row].annotations[res].description.length() > 0) + if (tip.length() != 6) { - this.setToolTipText(JvSwingUtils.wrapTooltip(true, - aa[row].annotations[res].description)); + tip.setLength(tip.length() - 4); + this.setToolTipText(tip.toString() + ""); } - else + } + else if (ann.annotations[column] != null) + { + String description = ann.annotations[column].description; + if (description != null && description.length() > 0) { - // clear the tooltip. - this.setToolTipText(null); + this.setToolTipText(JvSwingUtils.wrapTooltip(true, description)); } + } + else + { + // clear the tooltip. + this.setToolTipText(null); + } + } - if (aa[row].annotations[res] != null) + /** + * Constructs and displays the status bar message + * + * @param column + * @param ann + */ + void setStatusMessage(int column, AlignmentAnnotation ann) + { + /* + * show alignment column and annotation description if any + */ + StringBuilder text = new StringBuilder(32); + text.append(MessageManager.getString("label.column")).append(" ") + .append(column + 1); + + if (ann.annotations[column] != null) + { + String description = ann.annotations[column].description; + if (description != null && description.trim().length() > 0) { - StringBuffer text = new StringBuffer("Sequence position " - + (res + 1)); + text.append(" ").append(description); + } + } - if (aa[row].annotations[res].description != null) + /* + * if the annotation is sequence-specific, show the sequence number + * in the alignment, and (if not a gap) the residue and position + */ + SequenceI seqref = ann.sequenceRef; + if (seqref != null) + { + int seqIndex = av.getAlignment().findIndex(seqref); + if (seqIndex != -1) + { + text.append(", ") + .append(MessageManager.getString("label.sequence")) + .append(" ") + .append(seqIndex + 1); + char residue = seqref.getCharAt(column); + if (!Comparison.isGap(residue)) { - text.append(" " + aa[row].annotations[res].description); + text.append(" "); + String name; + if (av.getAlignment().isNucleotide()) + { + name = ResidueProperties.nucleotideName.get(String + .valueOf(residue)); + text.append(" Nucleotide: ").append( + name != null ? name : residue); + } + else + { + name = 'X' == residue ? "X" : ('*' == residue ? "STOP" + : ResidueProperties.aa2Triplet.get(String + .valueOf(residue))); + text.append(" Residue: ").append(name != null ? name : residue); + } + int residuePos = seqref.findPosition(column); + text.append(" (").append(residuePos).append(")"); } - - ap.alignFrame.statusBar.setText(text.toString()); } } - else - { - this.setToolTipText(null); - } + + ap.alignFrame.statusBar.setText(text.toString()); } /** diff --git a/src/jalview/gui/AnnotationRowFilter.java b/src/jalview/gui/AnnotationRowFilter.java index 17298ba..f0bea60 100644 --- a/src/jalview/gui/AnnotationRowFilter.java +++ b/src/jalview/gui/AnnotationRowFilter.java @@ -257,11 +257,11 @@ public abstract class AnnotationRowFilter extends JPanel protected void populateThresholdComboBox(JComboBox threshold) { threshold.addItem(MessageManager - .getString("label.threshold_feature_no_thereshold")); + .getString("label.threshold_feature_no_threshold")); threshold.addItem(MessageManager - .getString("label.threshold_feature_above_thereshold")); + .getString("label.threshold_feature_above_threshold")); threshold.addItem(MessageManager - .getString("label.threshold_feature_below_thereshold")); + .getString("label.threshold_feature_below_threshold")); } protected void seqAssociated_actionPerformed(ActionEvent arg0, diff --git a/src/jalview/gui/BlogReader.java b/src/jalview/gui/BlogReader.java index 2e72e17..ab9aad7 100644 --- a/src/jalview/gui/BlogReader.java +++ b/src/jalview/gui/BlogReader.java @@ -41,6 +41,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; +import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -285,6 +286,7 @@ public class BlogReader extends JPanel /** * check if the news panel's container is visible */ + @Override public boolean isVisible() { if (parent == null) @@ -312,6 +314,7 @@ public class BlogReader extends JPanel xf.setContentPane(me); xf.addWindowListener(new WindowAdapter() { + @Override public void windowClosing(WindowEvent e) { ActionEvent actionEvent = new ActionEvent(this, @@ -320,6 +323,7 @@ public class BlogReader extends JPanel exitAction.actionPerformed(actionEvent); } + @Override public void windowOpened(WindowEvent e) { } @@ -367,9 +371,9 @@ public class BlogReader extends JPanel + " and lastDate is " + lastDate); for (Item i : (List) chan.getItems()) { + Date published = i.getPublishDate(); boolean isread = lastDate == null ? false - : (i.getPublishDate() != null && !lastDate.before(i - .getPublishDate())); + : (published != null && !lastDate.before(published)); if (!updating || updateItems) { @@ -379,11 +383,11 @@ public class BlogReader extends JPanel { i.setRead(isread); } - if (i.getPublishDate() != null && !i.isRead()) + if (published != null && !i.isRead()) { - if (earliest == null || earliest.after(i.getPublishDate())) + if (earliest == null || earliest.after(published)) { - earliest = i.getPublishDate(); + earliest = published; } } } @@ -420,11 +424,9 @@ public class BlogReader extends JPanel } if (lastDate != null) { - jalview.bin.Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED", - lastDate); - jalview.bin.Cache.log.debug("Saved last read date as " - + jalview.bin.Cache.date_format.format(lastDate)); - + String formatted = Cache.setDateProperty( + "JALVIEW_NEWS_RSS_LASTMODIFIED", lastDate); + Cache.log.debug("Saved last read date as " + formatted); } } } @@ -472,6 +474,7 @@ public class BlogReader extends JPanel listItems.addMouseListener(new java.awt.event.MouseAdapter() { + @Override public void mouseClicked(MouseEvent e) { listItems_mouseClicked(e); @@ -497,6 +500,7 @@ public class BlogReader extends JPanel } textDescription.addHyperlinkListener(new HyperlinkListener() { + @Override public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) @@ -508,6 +512,7 @@ public class BlogReader extends JPanel listItems.addListSelectionListener(new ListSelectionListener() { + @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false) @@ -537,6 +542,7 @@ public class BlogReader extends JPanel _listItems = listItems; } + @Override public void actionPerformed(ActionEvent e) { Object o = _listItems.getSelectedValue(); @@ -550,6 +556,7 @@ public class BlogReader extends JPanel } } + @Override public void update(Object o) { setEnabled(true); @@ -754,11 +761,10 @@ public class BlogReader extends JPanel lastread.set(1983, 01, 01); while (lastread.before(today)) { - Cache.setDateProperty("JALVIEW_NEWS_RSS_LASTMODIFIED", - lastread.getTime()); + String formattedDate = Cache.setDateProperty( + "JALVIEW_NEWS_RSS_LASTMODIFIED", lastread.getTime()); BlogReader me = new BlogReader(); - System.out.println("Set last date to " - + jalview.bin.Cache.date_format.format(lastread.getTime())); + System.out.println("Set last date to " + formattedDate); if (me.isNewsNew()) { Cache.log.debug("There is news to read."); @@ -810,6 +816,7 @@ class ChannelsRenderer extends DefaultListCellRenderer private final static Icon _icon = new ImageIcon( Main.class.getResource("image/ComposeMail16.gif")); + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { @@ -837,6 +844,7 @@ class ItemsRenderer extends DefaultListCellRenderer private final static Icon _icon = new ImageIcon( Main.class.getResource("image/ComposeMail16.gif")); + @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index f2244d5..592f56c 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -249,6 +249,7 @@ public class ChimeraViewFrame extends StructureViewerBase SequenceI[][] seqs) { createProgressBar(); + // FIXME extractChains needs pdbentries to match IDs to PDBEntry(s) on seqs String[][] chains = extractChains(seqs); jmb = new JalviewChimeraBindingModel(this, ap.getStructureSelectionManager(), pdbentrys, seqs, chains, @@ -303,6 +304,9 @@ public class ChimeraViewFrame extends StructureViewerBase .getAllPDBEntries(); if (pdbrefs != null && pdbrefs.size() > 0) { + // FIXME: SequenceI.PDBEntry[0] chain mapping used for + // ChimeraViewFrame. Is this even used ??? + chain = pdbrefs.get(0).getChainCode(); } } @@ -728,6 +732,7 @@ public class ChimeraViewFrame extends StructureViewerBase */ private String fetchPdbFile(PDBEntry processingEntry) throws Exception { + // FIXME: this is duplicated code with Jmol frame ? String filePath = null; Pdb pdbclient = new Pdb(); AlignmentI pdbseq = null; diff --git a/src/jalview/gui/CrossRefAction.java b/src/jalview/gui/CrossRefAction.java new file mode 100644 index 0000000..32af226 --- /dev/null +++ b/src/jalview/gui/CrossRefAction.java @@ -0,0 +1,312 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.gui; + +import jalview.analysis.AlignmentUtils; +import jalview.analysis.CrossRef; +import jalview.api.AlignmentViewPanel; +import jalview.api.FeatureSettingsModelI; +import jalview.bin.Cache; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.DBRefSource; +import jalview.datamodel.SequenceI; +import jalview.io.gff.SequenceOntologyI; +import jalview.structure.StructureSelectionManager; +import jalview.util.MessageManager; +import jalview.ws.SequenceFetcher; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JOptionPane; + +/** + * Factory constructor and runnable for discovering and displaying + * cross-references for a set of aligned sequences + * + * @author jprocter + * + */ +public class CrossRefAction implements Runnable +{ + private AlignFrame alignFrame; + + private SequenceI[] sel; + + private boolean _odna; + + private String source; + + List xrefViews = new ArrayList(); + + public List getXrefViews() + { + return xrefViews; + } + + @Override + public void run() + { + final long sttime = System.currentTimeMillis(); + alignFrame.setProgressBar( + MessageManager.formatMessage( + "status.searching_for_sequences_from", + new Object[] { source }), sttime); + try + { + AlignmentI alignment = alignFrame.getViewport().getAlignment(); + AlignmentI dataset = alignment.getDataset() == null ? alignment + : alignment.getDataset(); + boolean dna = alignment.isNucleotide(); + if (_odna != dna) + { + System.err + .println("Conflict: showProducts for alignment originally " + + "thought to be " + (_odna ? "DNA" : "Protein") + + " now searching for " + (dna ? "DNA" : "Protein") + + " Context."); + } + AlignmentI xrefs = new CrossRef(sel, dataset).findXrefSequences( + source, dna); + if (xrefs == null) + { + return; + } + /* + * get display scheme (if any) to apply to features + */ + FeatureSettingsModelI featureColourScheme = new SequenceFetcher() + .getFeatureColourScheme(source); + + AlignmentI xrefsAlignment = makeCrossReferencesAlignment(dataset, + xrefs); + if (!dna) + { + xrefsAlignment = AlignmentUtils.makeCdsAlignment( + xrefsAlignment.getSequencesArray(), dataset, sel); + xrefsAlignment.alignAs(alignment); + } + + /* + * If we are opening a splitframe, make a copy of this alignment (sharing the same dataset + * sequences). If we are DNA, drop introns and update mappings + */ + AlignmentI copyAlignment = null; + + if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true)) + { + boolean copyAlignmentIsAligned = false; + if (dna) + { + copyAlignment = AlignmentUtils.makeCdsAlignment(sel, dataset, + xrefsAlignment.getSequencesArray()); + if (copyAlignment.getHeight() == 0) + { + JOptionPane.showMessageDialog(alignFrame, + MessageManager.getString("label.cant_map_cds"), + MessageManager.getString("label.operation_failed"), + JOptionPane.OK_OPTION); + System.err.println("Failed to make CDS alignment"); + } + + /* + * pending getting Embl transcripts to 'align', + * we are only doing this for Ensembl + */ + // TODO proper criteria for 'can align as cdna' + if (DBRefSource.ENSEMBL.equalsIgnoreCase(source) + || AlignmentUtils.looksLikeEnsembl(alignment)) + { + copyAlignment.alignAs(alignment); + copyAlignmentIsAligned = true; + } + } + else + { + copyAlignment = AlignmentUtils.makeCopyAlignment(sel, + xrefs.getSequencesArray(), dataset); + } + copyAlignment + .setGapCharacter(alignFrame.viewport.getGapCharacter()); + + StructureSelectionManager ssm = StructureSelectionManager + .getStructureSelectionManager(Desktop.instance); + + /* + * register any new mappings for sequence mouseover etc + * (will not duplicate any previously registered mappings) + */ + ssm.registerMappings(dataset.getCodonFrames()); + + if (copyAlignment.getHeight() <= 0) + { + System.err.println("No Sequences generated for xRef type " + + source); + return; + } + /* + * align protein to dna + */ + if (dna && copyAlignmentIsAligned) + { + xrefsAlignment.alignAs(copyAlignment); + } + else + { + /* + * align cdna to protein - currently only if + * fetching and aligning Ensembl transcripts! + */ + // TODO: generalise for other sources of locus/transcript/cds data + if (dna && DBRefSource.ENSEMBL.equalsIgnoreCase(source)) + { + copyAlignment.alignAs(xrefsAlignment); + } + } + } + /* + * build AlignFrame(s) according to available alignment data + */ + AlignFrame newFrame = new AlignFrame(xrefsAlignment, + AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); + if (Cache.getDefault("HIDE_INTRONS", true)) + { + newFrame.hideFeatureColumns(SequenceOntologyI.EXON, false); + } + String newtitle = String.format("%s %s %s", + dna ? MessageManager.getString("label.proteins") + : MessageManager.getString("label.nucleotides"), + MessageManager.getString("label.for"), alignFrame.getTitle()); + newFrame.setTitle(newtitle); + + if (copyAlignment == null) + { + /* + * split frame display is turned off in preferences file + */ + Desktop.addInternalFrame(newFrame, newtitle, + AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); + xrefViews.add(newFrame.alignPanel); + return; // via finally clause + } + AlignFrame copyThis = new AlignFrame(copyAlignment, + AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); + copyThis.setTitle(alignFrame.getTitle()); + + boolean showSequenceFeatures = alignFrame.getViewport() + .isShowSequenceFeatures(); + newFrame.setShowSeqFeatures(showSequenceFeatures); + copyThis.setShowSeqFeatures(showSequenceFeatures); + FeatureRenderer myFeatureStyling = alignFrame.alignPanel + .getSeqPanel().seqCanvas.getFeatureRenderer(); + + /* + * copy feature rendering settings to split frame + */ + newFrame.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer() + .transferSettings(myFeatureStyling); + copyThis.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer() + .transferSettings(myFeatureStyling); + + /* + * apply 'database source' feature configuration + * if any was found + */ + // TODO is this the feature colouring for the original + // alignment or the fetched xrefs? either could be Ensembl + newFrame.getViewport().applyFeaturesStyle(featureColourScheme); + copyThis.getViewport().applyFeaturesStyle(featureColourScheme); + + SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame, + dna ? newFrame : copyThis); + newFrame.setVisible(true); + copyThis.setVisible(true); + String linkedTitle = MessageManager + .getString("label.linked_view_title"); + Desktop.addInternalFrame(sf, linkedTitle, -1, -1); + sf.adjustDivider(); + + // finally add the top, then bottom frame to the view list + xrefViews.add(dna ? copyThis.alignPanel : newFrame.alignPanel); + xrefViews.add(!dna ? copyThis.alignPanel : newFrame.alignPanel); + + } catch (OutOfMemoryError e) + { + new OOMWarning("whilst fetching crossreferences", e); + } catch (Throwable e) + { + Cache.log.error("Error when finding crossreferences", e); + } finally + { + alignFrame.setProgressBar(MessageManager.formatMessage( + "status.finished_searching_for_sequences_from", + new Object[] { source }), sttime); + } + } + + /** + * Makes an alignment containing the given sequences, and adds them to the + * given dataset, which is also set as the dataset for the new alignment + * + * TODO: refactor to DatasetI method + * + * @param dataset + * @param seqs + * @return + */ + protected AlignmentI makeCrossReferencesAlignment(AlignmentI dataset, + AlignmentI seqs) + { + SequenceI[] sprods = new SequenceI[seqs.getHeight()]; + for (int s = 0; s < sprods.length; s++) + { + sprods[s] = (seqs.getSequenceAt(s)).deriveSequence(); + if (dataset.getSequences() == null + || !dataset.getSequences().contains( + sprods[s].getDatasetSequence())) + { + dataset.addSequence(sprods[s].getDatasetSequence()); + } + sprods[s].updatePDBIds(); + } + Alignment al = new Alignment(sprods); + al.setDataset(dataset); + return al; + } + + public CrossRefAction(AlignFrame alignFrame, SequenceI[] sel, + boolean _odna, String source) + { + this.alignFrame = alignFrame; + this.sel = sel; + this._odna = _odna; + this.source = source; + } + + public static CrossRefAction showProductsFor(final SequenceI[] sel, + final boolean _odna, final String source, + final AlignFrame alignFrame) + { + return new CrossRefAction(alignFrame, sel, _odna, source); + } + +} diff --git a/src/jalview/gui/CutAndPasteHtmlTransfer.java b/src/jalview/gui/CutAndPasteHtmlTransfer.java index 65d8670..dae83d1 100644 --- a/src/jalview/gui/CutAndPasteHtmlTransfer.java +++ b/src/jalview/gui/CutAndPasteHtmlTransfer.java @@ -228,6 +228,7 @@ public class CutAndPasteHtmlTransfer extends GCutAndPasteHtmlTransfer @Override public void textarea_mousePressed(MouseEvent e) { + // isPopupTrigger is on mousePressed (Mac) or mouseReleased (Windows) if (e.isPopupTrigger()) { JPopupMenu popup = new JPopupMenu( diff --git a/src/jalview/gui/CutAndPasteTransfer.java b/src/jalview/gui/CutAndPasteTransfer.java index 1161340..ed2b9bf 100644 --- a/src/jalview/gui/CutAndPasteTransfer.java +++ b/src/jalview/gui/CutAndPasteTransfer.java @@ -358,6 +358,10 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer @Override public void textarea_mousePressed(MouseEvent e) { + /* + * isPopupTrigger is checked in mousePressed on Mac, + * in mouseReleased on Windows + */ if (e.isPopupTrigger()) { JPopupMenu popup = new JPopupMenu( diff --git a/src/jalview/gui/DasSourceBrowser.java b/src/jalview/gui/DasSourceBrowser.java index b176672..e677084 100644 --- a/src/jalview/gui/DasSourceBrowser.java +++ b/src/jalview/gui/DasSourceBrowser.java @@ -448,7 +448,7 @@ public class DasSourceBrowser extends GDasSourceBrowser implements seqs.setSelected(seqsrc); JPanel panel = new JPanel(new BorderLayout()); JPanel pane12 = new JPanel(new BorderLayout()); - pane12.add(new JLabel(MessageManager.getString("label.name")), + pane12.add(new JLabel(MessageManager.getString("label.name:")), BorderLayout.CENTER); pane12.add(nametf, BorderLayout.EAST); panel.add(pane12, BorderLayout.NORTH); diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index 7f36f7f..387a2a9 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -61,6 +61,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; @@ -82,6 +83,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; +import javax.swing.AbstractAction; import javax.swing.DefaultDesktopManager; import javax.swing.DesktopManager; import javax.swing.JButton; @@ -96,6 +98,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; +import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkEvent.EventType; @@ -169,8 +172,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements static final int yOffset = 30; - private static AlignFrame currentAlignFrame; - public static jalview.ws.jws1.Discoverer discoverer; public static Object[] jalviewClipboard; @@ -396,7 +397,16 @@ public class Desktop extends jalview.jbgui.GDesktop implements @Override public void mousePressed(MouseEvent evt) { - if (evt.isPopupTrigger()) + if (evt.isPopupTrigger()) // Mac + { + showPasteMenu(evt.getX(), evt.getY()); + } + } + + @Override + public void mouseReleased(MouseEvent evt) + { + if (evt.isPopupTrigger()) // Windows { showPasteMenu(evt.getX(), evt.getY()); } @@ -824,7 +834,15 @@ public class Desktop extends jalview.jbgui.GDesktop implements javax.swing.event.InternalFrameEvent evt) { PaintRefresher.RemoveComponent(frame); - openFrameCount--; + + /* + * defensive check to prevent frames being + * added half off the window + */ + if (openFrameCount > 0) + { + openFrameCount--; + } windowMenu.remove(menuItem); JInternalFrame itf = desktop.getSelectedFrame(); if (itf != null) @@ -952,53 +970,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 files = new ArrayList(); + java.util.List protocols = new ArrayList(); 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; } @@ -1218,6 +1198,13 @@ public class Desktop extends jalview.jbgui.GDesktop implements dialogExecutor.shutdownNow(); } closeAll_actionPerformed(null); + + if (groovyConsole != null) + { + // suppress a possible repeat prompt to save script + groovyConsole.setDirty(false); + groovyConsole.exit(); + } System.exit(0); } @@ -1836,7 +1823,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements * * @param af */ - public void explodeViews(AlignFrame af) + public static void explodeViews(AlignFrame af) { int size = af.alignPanels.size(); if (size < 2) @@ -2409,7 +2396,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements if (Jalview.isHeadlessMode()) { // Desktop.desktop is null in headless mode - return new AlignFrame[] { currentAlignFrame }; + return new AlignFrame[] { Jalview.currentAlignFrame }; } JInternalFrame[] frames = Desktop.desktop.getAllFrames(); @@ -2505,50 +2492,79 @@ public class Desktop extends jalview.jbgui.GDesktop implements */ void openGroovyConsole() { - groovyConsole = new groovy.ui.Console(); + if (groovyConsole == null) + { + groovyConsole = new groovy.ui.Console(); + groovyConsole.setVariable("Jalview", this); + groovyConsole.run(); - /* - * bind groovy variable 'Jalview' to the Desktop object - */ - groovyConsole.setVariable("Jalview", this); + /* + * We allow only one console at a time, so that AlignFrame menu option + * 'Calculate | Run Groovy script' is unambiguous. + * Disable 'Groovy 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) + { + /* + * rebind CMD-Q from Groovy Console to Jalview Quit + */ + addQuitHandler(); + enableExecuteGroovy(false); + } + }); + } /* - * start the console + * show Groovy console window (after close and reopen) */ - groovyConsole.run(); + ((Window) groovyConsole.getFrame()).setVisible(true); /* - * 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 + * if we got this far, enable 'Run Groovy' in AlignFrame menus + * and disable opening a second console */ - Window window = (Window) groovyConsole.getFrame(); - window.addWindowListener(new WindowAdapter() + enableExecuteGroovy(true); + } + + /** + * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this + * binding when opened + */ + protected void addQuitHandler() + { + getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( + KeyStroke.getKeyStroke(KeyEvent.VK_Q, Toolkit + .getDefaultToolkit().getMenuShortcutKeyMask()), + "Quit"); + getRootPane().getActionMap().put("Quit", new AbstractAction() { @Override - public void windowClosed(WindowEvent e) + public void actionPerformed(ActionEvent e) { - groovyShell.setEnabled(true); - enableExecuteGroovy(false); + quit(); } }); - - /* - * 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 + * true if Groovy console is open */ public void enableExecuteGroovy(boolean enabled) { + /* + * disable opening a second Groovy console + * (or re-enable when the console is closed) + */ + groovyShell.setEnabled(!enabled); + AlignFrame[] alignFrames = getAlignFrames(); if (alignFrames != null) { @@ -3172,22 +3188,109 @@ public class Desktop extends jalview.jbgui.GDesktop implements * The dust settles...give focus to the tab we did this from. */ myTopFrame.setDisplayedView(myTopFrame.alignPanel); - } - public static AlignFrame getCurrentAlignFrame() + public static groovy.ui.Console getGroovyConsole() { - return currentAlignFrame; + return groovyConsole; } - public static void setCurrentAlignFrame(AlignFrame currentAlignFrame) + public static void transferFromDropTarget(List files, + List protocols, DropTargetDropEvent evt, Transferable t) + throws Exception { - Desktop.currentAlignFrame = currentAlignFrame; - } - public static groovy.ui.Console getGroovyConsole() - { - return groovyConsole; + 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"); + } + } + } + } + } } - } diff --git a/src/jalview/gui/FeatureColourChooser.java b/src/jalview/gui/FeatureColourChooser.java index ac22aca..4c4c69a 100644 --- a/src/jalview/gui/FeatureColourChooser.java +++ b/src/jalview/gui/FeatureColourChooser.java @@ -247,11 +247,11 @@ public class FeatureColourChooser extends JalviewDialog threshold.setToolTipText(MessageManager .getString("label.threshold_feature_display_by_score")); threshold.addItem(MessageManager - .getString("label.threshold_feature_no_thereshold")); // index 0 + .getString("label.threshold_feature_no_threshold")); // index 0 threshold.addItem(MessageManager - .getString("label.threshold_feature_above_thereshold")); // index 1 + .getString("label.threshold_feature_above_threshold")); // index 1 threshold.addItem(MessageManager - .getString("label.threshold_feature_below_thereshold")); // index 2 + .getString("label.threshold_feature_below_threshold")); // index 2 jPanel3.setLayout(flowLayout2); thresholdValue.addActionListener(new ActionListener() { @@ -268,7 +268,7 @@ public class FeatureColourChooser extends JalviewDialog slider.setOpaque(false); slider.setPreferredSize(new Dimension(100, 32)); slider.setToolTipText(MessageManager - .getString("label.adjust_thereshold")); + .getString("label.adjust_threshold")); thresholdValue.setEnabled(false); thresholdValue.setColumns(7); jPanel3.setBackground(Color.white); @@ -420,7 +420,6 @@ public class FeatureColourChooser extends JalviewDialog acg = new FeatureColour(new Colour( oldminColour = minColour.getBackground()), new Colour( oldmaxColour = maxColour.getBackground()), min, max); - } if (!hasThreshold) diff --git a/src/jalview/gui/FeatureRenderer.java b/src/jalview/gui/FeatureRenderer.java index 0284105..3c4d733 100644 --- a/src/jalview/gui/FeatureRenderer.java +++ b/src/jalview/gui/FeatureRenderer.java @@ -77,9 +77,8 @@ public class FeatureRenderer extends */ public FeatureRenderer(AlignmentPanel ap) { - super(); + super(ap.av); this.ap = ap; - this.av = ap.av; if (ap != null && ap.getSeqPanel() != null && ap.getSeqPanel().seqCanvas != null && ap.getSeqPanel().seqCanvas.fr != null) @@ -176,7 +175,8 @@ public class FeatureRenderer extends { panel = new JPanel(new GridLayout(4, 1)); tmp = new JPanel(); - tmp.add(new JLabel(MessageManager.getString("label.select_feature"))); + tmp.add(new JLabel(MessageManager.getString("label.select_feature") + + ":")); overlaps = new JComboBox(); for (int i = 0; i < features.length; i++) { @@ -227,12 +227,13 @@ public class FeatureRenderer extends tmp = new JPanel(); panel.add(tmp); - tmp.add(new JLabel(MessageManager.getString("label.name"), JLabel.RIGHT)); + tmp.add(new JLabel(MessageManager.getString("label.name:"), + JLabel.RIGHT)); tmp.add(name); tmp = new JPanel(); panel.add(tmp); - tmp.add(new JLabel(MessageManager.getString("label.group") + ":", + tmp.add(new JLabel(MessageManager.getString("label.group:"), JLabel.RIGHT)); tmp.add(source); @@ -251,7 +252,7 @@ public class FeatureRenderer extends bigPanel.add(panel, BorderLayout.NORTH); panel = new JPanel(); - panel.add(new JLabel(MessageManager.getString("label.description"), + panel.add(new JLabel(MessageManager.getString("label.description:"), JLabel.RIGHT)); description.setFont(JvSwingUtils.getTextAreaFont()); description.setLineWrap(true); @@ -425,6 +426,10 @@ public class FeatureRenderer extends * @param bigPanel * @param colour * @param col +<<<<<<< HEAD +======= + * @param col +>>>>>>> refs/heads/develop */ protected void updateColourButton(JPanel bigPanel, JLabel colour, FeatureColourI col) diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index 187d3d0..62aba6d 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -184,14 +184,16 @@ public class FeatureSettings extends JPanel implements } else if (evt.getClickCount() == 2) { + boolean invertSelection = evt.isAltDown(); + boolean toggleSelection = Platform.isControlDown(evt); + boolean extendSelection = evt.isShiftDown(); fr.ap.alignFrame.avc.markColumnsContainingFeatures( - evt.isAltDown(), evt.isShiftDown() || evt.isMetaDown(), - evt.isMetaDown(), + invertSelection, extendSelection, toggleSelection, (String) table.getValueAt(selectedRow, 0)); } } - // isPopupTrigger fires on mouseReleased on Mac + // isPopupTrigger fires on mouseReleased on Windows @Override public void mouseReleased(MouseEvent evt) { @@ -213,11 +215,20 @@ public class FeatureSettings extends JPanel implements int newRow = table.rowAtPoint(evt.getPoint()); if (newRow != selectedRow && selectedRow != -1 && newRow != -1) { + /* + * reposition 'selectedRow' to 'newRow' (the dragged to location) + * this could be more than one row away for a very fast drag action + * so just swap it with adjacent rows until we get it there + */ Object[][] data = ((FeatureTableModel) table.getModel()) .getData(); - Object[] temp = data[selectedRow]; - data[selectedRow] = data[newRow]; - data[newRow] = temp; + int direction = newRow < selectedRow ? -1 : 1; + for (int i = selectedRow; i != newRow; i += direction) + { + Object[] temp = data[i]; + data[i] = data[i + direction]; + data[i + direction] = temp; + } updateFeatureRenderer(data); table.repaint(); selectedRow = newRow; @@ -386,7 +397,9 @@ public class FeatureSettings extends JPanel implements else { // probably the color chooser! - table.setValueAt(colorChooser.getColor(), selectedRow, 1); + table.setValueAt( + new FeatureColour(colorChooser.getColor()), + selectedRow, 1); table.validate(); me.updateFeatureRenderer( ((FeatureTableModel) table.getModel()).getData(), @@ -835,7 +848,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"); @@ -1525,8 +1538,6 @@ public class FeatureSettings extends JPanel implements this.setIcon(null); newColor = ColorUtils.getColor(cellColour.getColour()); setBackground(newColor); - // comp.setToolTipText("RGB value: " + newColor.getRed() + ", " - // + newColor.getGreen() + ", " + newColor.getBlue()); } if (isSelected) { diff --git a/src/jalview/gui/FontChooser.java b/src/jalview/gui/FontChooser.java index 535196e..1f6c068 100755 --- a/src/jalview/gui/FontChooser.java +++ b/src/jalview/gui/FontChooser.java @@ -157,6 +157,7 @@ public class FontChooser extends GFontChooser init = false; } + @Override public void smoothFont_actionPerformed(ActionEvent e) { ap.av.antiAlias = smoothFont.isSelected(); @@ -170,6 +171,7 @@ public class FontChooser extends GFontChooser * @param e * DOCUMENT ME! */ + @Override protected void ok_actionPerformed(ActionEvent e) { try @@ -194,6 +196,7 @@ public class FontChooser extends GFontChooser * @param e * DOCUMENT ME! */ + @Override protected void cancel_actionPerformed(ActionEvent e) { if (ap != null) @@ -247,10 +250,10 @@ public class FontChooser extends GFontChooser double iw = iBounds.getWidth(); if (mw < 1 || iw < 1) { - final String messageKey = iBounds.getHeight() < 1 ? "label.font_doesnt_have_letters_defined" - : "label.font_too_small"; - JOptionPane.showInternalMessageDialog(this, - MessageManager.getString(messageKey), + String message = iBounds.getHeight() < 1 ? MessageManager + .getString("label.font_doesnt_have_letters_defined") + : MessageManager.getString("label.font_too_small"); + JOptionPane.showInternalMessageDialog(this, message, MessageManager.getString("label.invalid_font"), JOptionPane.WARNING_MESSAGE); /* @@ -301,6 +304,7 @@ public class FontChooser extends GFontChooser * @param e * DOCUMENT ME! */ + @Override protected void fontName_actionPerformed(ActionEvent e) { if (init) @@ -317,6 +321,7 @@ public class FontChooser extends GFontChooser * @param e * DOCUMENT ME! */ + @Override protected void fontSize_actionPerformed(ActionEvent e) { if (init) @@ -333,6 +338,7 @@ public class FontChooser extends GFontChooser * @param e * DOCUMENT ME! */ + @Override protected void fontStyle_actionPerformed(ActionEvent e) { if (init) @@ -349,6 +355,7 @@ public class FontChooser extends GFontChooser * * @param e */ + @Override public void defaultButton_actionPerformed(ActionEvent e) { Cache.setProperty("FONT_NAME", fontName.getSelectedItem().toString()); diff --git a/src/jalview/gui/IdPanel.java b/src/jalview/gui/IdPanel.java index c84505b..05166ad 100755 --- a/src/jalview/gui/IdPanel.java +++ b/src/jalview/gui/IdPanel.java @@ -26,6 +26,7 @@ import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.io.SequenceAnnotationReport; import jalview.util.MessageManager; +import jalview.util.Platform; import jalview.util.UrlLink; import jalview.viewmodel.AlignmentViewport; @@ -316,49 +317,32 @@ public class IdPanel extends JPanel implements MouseListener, return; } - int seq = alignPanel.getSeqPanel().findSeq(e); - - if (e.isPopupTrigger()) + if (e.isPopupTrigger()) // Mac reports this in mousePressed { - 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 sfs[] = sq == null ? null : sq.getSequenceFeatures(); - if (sfs != null) - { - for (SequenceFeature sf : sfs) - { - if (sf.begin == sf.end && sf.begin == 0) - { - if (sf.links != null && sf.links.size() > 0) - { - for (int l = 0, lSize = sf.links.size(); l < lSize; l++) - { - nlinks.addElement(sf.links.elementAt(l)); - } - } - } - } - } - - PopupMenu pop = new PopupMenu(alignPanel, sq, nlinks, - Preferences.getGroupURLLinks()); - pop.show(this, e.getX(), e.getY()); + showPopupMenu(e); + return; + } + /* + * defer right-mouse click handling to mouseReleased on Windows + * (where isPopupTrigger() will answer true) + * NB isRightMouseButton is also true for Cmd-click on Mac + */ + if (SwingUtilities.isRightMouseButton(e) && !Platform.isAMac()) + { return; } if ((av.getSelectionGroup() == null) - || ((!e.isControlDown() && !e.isShiftDown()) && av - .getSelectionGroup() != null)) + || (!jalview.util.Platform.isControlDown(e) + && !e.isShiftDown() && av.getSelectionGroup() != null)) { av.setSelectionGroup(new SequenceGroup()); av.getSelectionGroup().setStartRes(0); av.getSelectionGroup().setEndRes(av.getAlignment().getWidth() - 1); } + int seq = alignPanel.getSeqPanel().findSeq(e); if (e.isShiftDown() && (lastid != -1)) { selectSeqs(lastid, seq); @@ -367,13 +351,49 @@ public class IdPanel extends JPanel implements MouseListener, { selectSeq(seq); } - // TODO is this addition ok here? + av.isSelectionGroupChanged(true); alignPanel.paintAlignment(true); } /** + * Build and show the popup-menu at the right-click mouse position + * + * @param e + */ + void showPopupMenu(MouseEvent e) + { + int seq2 = alignPanel.getSeqPanel().findSeq(e); + Sequence sq = (Sequence) av.getAlignment().getSequenceAt(seq2); + // build a new links menu based on the current links + any non-positional + // features + Vector nlinks = new Vector( + Preferences.sequenceURLLinks); + SequenceFeature sfs[] = sq == null ? null : sq.getSequenceFeatures(); + if (sfs != null) + { + for (SequenceFeature sf : sfs) + { + if (sf.begin == sf.end && sf.begin == 0) + { + if (sf.links != null && sf.links.size() > 0) + { + for (int l = 0, lSize = sf.links.size(); l < lSize; l++) + { + nlinks.addElement(sf.links.elementAt(l)); + } + } + } + } + } + + PopupMenu pop = new PopupMenu(alignPanel, sq, nlinks, + Preferences.getGroupURLLinks()); + pop.show(this, e.getX(), e.getY()); + } + + /** * Toggle whether the sequence is part of the current selection group. * * @param seq @@ -440,6 +460,11 @@ public class IdPanel extends JPanel implements MouseListener, PaintRefresher.Refresh(this, av.getSequenceSetId()); // always send selection message when mouse is released av.sendSelection(); + + if (e.isPopupTrigger()) // Windows reports this in mouseReleased + { + showPopupMenu(e); + } } /** diff --git a/src/jalview/gui/JDatabaseTree.java b/src/jalview/gui/JDatabaseTree.java index 6f602ad..2a3d788 100644 --- a/src/jalview/gui/JDatabaseTree.java +++ b/src/jalview/gui/JDatabaseTree.java @@ -26,7 +26,6 @@ import jalview.ws.seqfetcher.DbSourceProxy; import java.awt.BorderLayout; import java.awt.Component; -import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; @@ -34,6 +33,8 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.HashSet; import java.util.Hashtable; @@ -73,7 +74,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener @Override public void actionPerformed(ActionEvent arg0) { - showDialog(null); + showDialog(); } }); return viewdbs; @@ -169,6 +170,19 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener _setSelectionState(); } }); + dbviews.addMouseListener(new MouseAdapter() + { + + @Override + public void mousePressed(MouseEvent e) + { + if (e.getClickCount() == 2) + { + okPressed(); + closeDialog(); + } + } + }); JPanel jc = new JPanel(new BorderLayout()), j = new JPanel( new FlowLayout()); jc.add(svp, BorderLayout.CENTER); @@ -188,6 +202,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener jc.validate(); // j.setPreferredSize(new Dimension(300,50)); add(jc, BorderLayout.CENTER); + ok.setEnabled(false); j.add(ok); j.add(cancel); add(j, BorderLayout.SOUTH); @@ -308,7 +323,6 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener protected void okPressed() { _setSelectionState(); - closeDialog(); } @Override @@ -320,7 +334,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener closeDialog(); } - private void showDialog(Container parent) + void showDialog() { oldselection = selection; oldtsel = tsel; @@ -349,10 +363,12 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener { return; } + ok.setEnabled(false); if (dbviews.getSelectionCount() == 0) { selection = null; } + tsel = dbviews.getSelectionPaths(); boolean forcedFirstChild = false; List srcs = new ArrayList(); @@ -364,6 +380,10 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener .getLastPathComponent(); if (dmt.getUserObject() != null) { + /* + * enable OK button once a selection has been made + */ + ok.setEnabled(true); if (dmt.getUserObject() instanceof DbSourceProxy) { srcs.add((DbSourceProxy) dmt.getUserObject()); @@ -419,6 +439,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener } } + dbstatex.setText(" "); if (allowMultiSelections) { dbstatus.setText(MessageManager.formatMessage( @@ -427,7 +448,6 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener (srcs.size() == 1 ? "" : "s"), (srcs.size() > 0 ? " with " + x + " test quer" + (x == 1 ? "y" : "ies") : ".") })); - dbstatex.setText(" "); } else { @@ -440,10 +460,6 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener dbstatex.setText(MessageManager.formatMessage( "label.example_param", new String[] { qr })); } - else - { - dbstatex.setText(" "); - } } else { @@ -555,6 +571,7 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener { action = arg0.getKeyCode(); okPressed(); + closeDialog(); } if (!arg0.isConsumed() && arg0.getKeyChar() == KeyEvent.VK_ESCAPE) { diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index ef5802e..e490c4e 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -81,6 +81,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; @@ -108,6 +109,7 @@ import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -119,7 +121,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; @@ -166,7 +167,9 @@ public class Jalview2XML */ Map seqRefIds = null; - Vector frefedSequence = null; + Map incompleteSeqs = null; + + List frefedSequence = null; boolean raiseGUI = true; // whether errors are raised in dialog boxes or not @@ -221,6 +224,10 @@ public class Jalview2XML { seqsToIds.clear(); } + if (incompleteSeqs != null) + { + incompleteSeqs.clear(); + } // seqRefIds = null; // seqsToIds = null; } @@ -243,6 +250,14 @@ public class Jalview2XML { seqRefIds = new HashMap(); } + if (incompleteSeqs == null) + { + incompleteSeqs = new HashMap(); + } + if (frefedSequence == null) + { + frefedSequence = new ArrayList(); + } } public Jalview2XML() @@ -254,78 +269,175 @@ public class Jalview2XML this.raiseGUI = raiseGUI; } - public void resolveFrefedSequences() + /** + * base class for resolving forward references to sequences by their ID + * + * @author jprocter + * + */ + abstract class SeqFref { - if (frefedSequence.size() > 0) + String sref; + + String type; + + public SeqFref(String _sref, String type) + { + sref = _sref; + this.type = type; + } + + public String getSref() + { + return sref; + } + + public SequenceI getSrefSeq() + { + return seqRefIds.get(sref); + } + + public boolean isResolvable() + { + return seqRefIds.get(sref) != null; + } + + public SequenceI getSrefDatasetSeq() { - int r = 0, rSize = frefedSequence.size(); - while (r < rSize) + SequenceI sq = seqRefIds.get(sref); + if (sq != null) { - Object[] ref = frefedSequence.elementAt(r); - if (ref != null) + while (sq.getDatasetSequence() != null) { - String sref = (String) ref[0]; - if (seqRefIds.containsKey(sref)) - { - if (ref[1] instanceof jalview.datamodel.Mapping) - { - SequenceI seq = seqRefIds.get(sref); - while (seq.getDatasetSequence() != null) - { - seq = seq.getDatasetSequence(); - } - ((jalview.datamodel.Mapping) ref[1]).setTo(seq); - } - else - { - if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame) - { - SequenceI seq = seqRefIds.get(sref); - while (seq.getDatasetSequence() != null) - { - seq = seq.getDatasetSequence(); - } - if (ref[2] != null - && ref[2] instanceof jalview.datamodel.Mapping) - { - jalview.datamodel.Mapping mp = (jalview.datamodel.Mapping) ref[2]; - ((jalview.datamodel.AlignedCodonFrame) ref[1]).addMap( - seq, mp.getTo(), mp.getMap()); - } - else - { - System.err - .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for AlcodonFrames involving " - + ref[2].getClass() + " type objects."); - } - } - else - { - System.err - .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for " - + ref[1].getClass() + " type objects."); - } - } - frefedSequence.remove(r); - rSize--; - } - else + sq = sq.getDatasetSequence(); + } + } + return sq; + } + /** + * @return true if the forward reference was fully resolved + */ + abstract boolean resolve(); + + @Override + public String toString() + { + return type + " reference to " + sref; + } + } + + /** + * create forward reference for a mapping + * + * @param sref + * @param _jmap + * @return + */ + public SeqFref newMappingRef(final String sref, + final jalview.datamodel.Mapping _jmap) + { + SeqFref fref = new SeqFref(sref, "Mapping") + { + public jalview.datamodel.Mapping jmap = _jmap; + + @Override + boolean resolve() + { + SequenceI seq = getSrefDatasetSeq(); + if (seq == null) + { + return false; + } + jmap.setTo(seq); + return true; + } + }; + return fref; + } + + public SeqFref newAlcodMapRef(final String sref, + final AlignedCodonFrame _cf, final jalview.datamodel.Mapping _jmap) + { + + SeqFref fref = new SeqFref(sref, "Codon Frame") + { + AlignedCodonFrame cf = _cf; + + public jalview.datamodel.Mapping mp = _jmap; + + @Override + public boolean isResolvable() + { + return super.isResolvable() && mp.getTo() != null; + }; + + @Override + boolean resolve() + { + SequenceI seq = getSrefDatasetSeq(); + if (seq == null) + { + return false; + } + cf.addMap(seq, mp.getTo(), mp.getMap()); + return true; + } + }; + return fref; + } + + public void resolveFrefedSequences() + { + Iterator nextFref=frefedSequence.iterator(); + int toresolve=frefedSequence.size(); + int unresolved=0,failedtoresolve=0; + while (nextFref.hasNext()) { + SeqFref ref = nextFref.next(); + if (ref.isResolvable()) + { + try { + if (ref.resolve()) { - System.err - .println("IMPLEMENTATION WARNING: Unresolved forward reference for hash string " - + ref[0] - + " with objecttype " - + ref[1].getClass()); - r++; + nextFref.remove(); + } else { + failedtoresolve++; } - } - else + } catch (Exception x) { + System.err.println("IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "+ref.getSref()); + x.printStackTrace(); + failedtoresolve++; + } + } else { + unresolved++; + } + } + if (unresolved>0) + { + System.err.println("Jalview Project Import: There were " + unresolved + + " forward references left unresolved on the stack."); + } + if (failedtoresolve>0) + { + System.err.println("SERIOUS! " + failedtoresolve + + " resolvable forward references failed to resolve."); + } + if (incompleteSeqs != null && incompleteSeqs.size() > 0) + { + System.err.println("Jalview Project Import: There are " + + incompleteSeqs.size() + + " sequences which may have incomplete metadata."); + if (incompleteSeqs.size() < 10) + { + for (SequenceI s : incompleteSeqs.values()) { - // empty reference - frefedSequence.remove(r); - rSize--; + System.err.println(s.toString()); } } + else + { + System.err + .println("Too many to report. Skipping output of incomplete sequences."); + } } } @@ -397,7 +509,20 @@ public class Jalview2XML { return; } + saveAllFrames(Arrays.asList(frames), jout); + } + /** + * core method for storing state for a set of AlignFrames. + * + * @param frames + * - frames involving all data to be exported (including containing + * splitframes) + * @param jout + * - project output stream + */ + private void saveAllFrames(List frames, JarOutputStream jout) + { Hashtable dsses = new Hashtable(); /* @@ -417,9 +542,9 @@ public class Jalview2XML List viewIds = new ArrayList(); // REVERSE ORDER - for (int i = frames.length - 1; i > -1; i--) + for (int i = frames.size() - 1; i > -1; i--) { - AlignFrame af = frames[i]; + AlignFrame af = frames.get(i); // skip ? if (skipList != null && skipList @@ -522,30 +647,20 @@ public class Jalview2XML { try { - int ap = 0; - int apSize = af.alignPanels.size(); FileOutputStream fos = new FileOutputStream(jarFile); JarOutputStream jout = new JarOutputStream(fos); - Hashtable dsses = new Hashtable(); - List viewIds = new ArrayList(); + List frames = new ArrayList(); - for (AlignmentPanel apanel : af.alignPanels) + // resolve splitframes + if (af.getViewport().getCodingComplement() != null) { - String jfileName = apSize == 1 ? fileName : fileName + ap; - ap++; - if (!jfileName.endsWith(".xml")) - { - jfileName = jfileName + ".xml"; - } - saveState(apanel, jfileName, jout, viewIds); - String dssid = getDatasetIdRef(af.getViewport().getAlignment() - .getDataset()); - if (!dsses.containsKey(dssid)) - { - dsses.put(dssid, af); - } + frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames(); + } + else + { + frames.add(af); } - writeDatasetFor(dsses, fileName, jout); + saveAllFrames(frames, jout); try { jout.flush(); @@ -636,11 +751,15 @@ public class Jalview2XML object.setVersion(jalview.bin.Cache.getDefault("VERSION", "Development Build")); - jalview.datamodel.AlignmentI jal = av.getAlignment(); + /** + * rjal is full height alignment, jal is actual alignment with full metadata + * but excludes hidden sequences. + */ + jalview.datamodel.AlignmentI rjal = av.getAlignment(), jal = rjal; if (av.hasHiddenRows()) { - jal = jal.getHiddenSequences().getFullAlignment(); + rjal = jal.getHiddenSequences().getFullAlignment(); } SequenceSet vamsasSet = new SequenceSet(); @@ -657,6 +776,7 @@ public class Jalview2XML { // switch jal and the dataset jal = jal.getDataset(); + rjal = jal; } } if (jal.getProperties() != null) @@ -674,38 +794,42 @@ public class Jalview2XML JSeq jseq; Set calcIdSet = new HashSet(); - + // record the set of vamsas sequence XML POJO we create. + HashMap vamsasSetIds = new HashMap(); // SAVE SEQUENCES - for (int i = 0; i < jal.getHeight(); i++) + for (final SequenceI jds : rjal.getSequences()) { - final SequenceI jds = jal.getSequenceAt(i); final SequenceI jdatasq = jds.getDatasetSequence() == null ? jds : jds.getDatasetSequence(); String id = seqHash(jds); - - if (seqRefIds.get(id) != null) - { - // This happens for two reasons: 1. multiple views are being serialised. - // 2. the hashCode has collided with another sequence's code. This DOES - // HAPPEN! (PF00072.15.stk does this) - // JBPNote: Uncomment to debug writing out of files that do not read - // back in due to ArrayOutOfBoundExceptions. - // System.err.println("vamsasSeq backref: "+id+""); - // System.err.println(jds.getName()+" - // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString()); - // System.err.println("Hashcode: "+seqHash(jds)); - // SequenceI rsq = (SequenceI) seqRefIds.get(id + ""); - // System.err.println(rsq.getName()+" - // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString()); - // System.err.println("Hashcode: "+seqHash(rsq)); - } - else - { - vamsasSeq = createVamsasSequence(id, jds); - vamsasSet.addSequence(vamsasSeq); - seqRefIds.put(id, jds); + if (vamsasSetIds.get(id) == null) + { + if (seqRefIds.get(id) != null && !storeDS) + { + // This happens for two reasons: 1. multiple views are being + // serialised. + // 2. the hashCode has collided with another sequence's code. This + // DOES + // HAPPEN! (PF00072.15.stk does this) + // JBPNote: Uncomment to debug writing out of files that do not read + // back in due to ArrayOutOfBoundExceptions. + // System.err.println("vamsasSeq backref: "+id+""); + // System.err.println(jds.getName()+" + // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString()); + // System.err.println("Hashcode: "+seqHash(jds)); + // SequenceI rsq = (SequenceI) seqRefIds.get(id + ""); + // System.err.println(rsq.getName()+" + // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString()); + // System.err.println("Hashcode: "+seqHash(rsq)); + } + else + { + vamsasSeq = createVamsasSequence(id, jds); + vamsasSet.addSequence(vamsasSeq); + vamsasSetIds.put(id, vamsasSeq); + seqRefIds.put(id, jds); + } } - jseq = new JSeq(); jseq.setStart(jds.getStart()); jseq.setEnd(jds.getEnd()); @@ -717,26 +841,34 @@ public class Jalview2XML // Store any sequences this sequence represents if (av.hasHiddenRows()) { + // use rjal, contains the full height alignment jseq.setHidden(av.getAlignment().getHiddenSequences() .isHidden(jds)); - if (av.isHiddenRepSequence(jal.getSequenceAt(i))) + if (av.isHiddenRepSequence(jds)) { jalview.datamodel.SequenceI[] reps = av - .getRepresentedSequences(jal.getSequenceAt(i)) - .getSequencesInOrder(jal); + .getRepresentedSequences(jds) + .getSequencesInOrder(rjal); for (int h = 0; h < reps.length; h++) { - if (reps[h] != jal.getSequenceAt(i)) + if (reps[h] != jds) { - jseq.addHiddenSequences(jal.findIndex(reps[h])); + jseq.addHiddenSequences(rjal.findIndex(reps[h])); } } } } + // mark sequence as reference - if it is the reference for this view + if (jal.hasSeqrep()) + { + jseq.setViewreference(jds == jal.getSeqrep()); + } } + // TODO: omit sequence features from each alignment view's XML dump if we + // are storing dataset if (jds.getSequenceFeatures() != null) { jalview.datamodel.SequenceFeature[] sf = jds.getSequenceFeatures(); @@ -881,16 +1013,17 @@ public class Jalview2XML jal = av.getAlignment(); } // SAVE MAPPINGS - if (jal.getCodonFrames() != null) + // FOR DATASET + if (storeDS && jal.getCodonFrames() != null) { List jac = jal.getCodonFrames(); for (AlignedCodonFrame acf : jac) { AlcodonFrame alc = new AlcodonFrame(); - vamsasSet.addAlcodonFrame(alc); if (acf.getProtMappings() != null && acf.getProtMappings().length > 0) { + boolean hasMap = false; SequenceI[] dnas = acf.getdnaSeqs(); jalview.datamodel.Mapping[] pmaps = acf.getProtMappings(); for (int m = 0; m < pmaps.length; m++) @@ -900,9 +1033,14 @@ public class Jalview2XML alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null, false)); alc.addAlcodMap(alcmap); + hasMap = true; + } + if (hasMap) + { + vamsasSet.addAlcodonFrame(alc); } } - + // TODO: delete this ? dead code from 2.8.3->2.9 ? // { // AlcodonFrame alc = new AlcodonFrame(); // vamsasSet.addAlcodonFrame(alc); @@ -1200,41 +1338,41 @@ public class Jalview2XML Vector settingsAdded = new Vector(); if (renderOrder != null) { - for (int ro = 0; ro < renderOrder.length; ro++) + for (String featureType : renderOrder) { - FeatureColourI gstyle = ap.getSeqPanel().seqCanvas + FeatureColourI fcol = ap.getSeqPanel().seqCanvas .getFeatureRenderer() - .getFeatureStyle(renderOrder[ro]); + .getFeatureStyle(featureType); Setting setting = new Setting(); - setting.setType(renderOrder[ro]); - if (!gstyle.isSimpleColour()) + setting.setType(featureType); + if (!fcol.isSimpleColour()) { - setting.setColour(gstyle.getMaxColour().getRGB()); - setting.setMincolour(gstyle.getMinColour().getRGB()); - setting.setMin(gstyle.getMin()); - setting.setMax(gstyle.getMax()); - setting.setColourByLabel(gstyle.isColourByLabel()); - setting.setAutoScale(gstyle.isAutoScaled()); - setting.setThreshold(gstyle.getThreshold()); + setting.setColour(fcol.getMaxColour().getRGB()); + setting.setMincolour(fcol.getMinColour().getRGB()); + setting.setMin(fcol.getMin()); + setting.setMax(fcol.getMax()); + setting.setColourByLabel(fcol.isColourByLabel()); + setting.setAutoScale(fcol.isAutoScaled()); + setting.setThreshold(fcol.getThreshold()); // -1 = No threshold, 0 = Below, 1 = Above - setting.setThreshstate(gstyle.isAboveThreshold() ? 1 - : (gstyle.isBelowThreshold() ? 0 : -1)); + setting.setThreshstate(fcol.isAboveThreshold() ? 1 + : (fcol.isBelowThreshold() ? 0 : -1)); } else { - setting.setColour(gstyle.getColour().getRGB()); + setting.setColour(fcol.getColour().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); } } @@ -1245,7 +1383,7 @@ public class Jalview2XML Vector groupsAdded = new Vector(); while (en.hasNext()) { - String grp = en.next().toString(); + String grp = en.next(); if (groupsAdded.contains(grp)) { continue; @@ -1924,16 +2062,17 @@ public class Jalview2XML if (jds.getDatasetSequence() != null) { vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence())); - if (jds.getDatasetSequence().getDBRefs() != null) - { - dbrefs = jds.getDatasetSequence().getDBRefs(); - } } else { - vamsasSeq.setDsseqid(id); // so we can tell which sequences really are + // seqId==dsseqid so we can tell which sequences really are // dataset sequences only + vamsasSeq.setDsseqid(id); dbrefs = jds.getDBRefs(); + if (parentseq == null) + { + parentseq = jds; + } } if (dbrefs != null) { @@ -1985,38 +2124,32 @@ public class Jalview2XML if (jmp.getTo() != null) { MappingChoice mpc = new MappingChoice(); - if (recurse - && (parentseq != jmp.getTo() || parentseq - .getDatasetSequence() != jmp.getTo())) + + // check/create ID for the sequence referenced by getTo() + + String jmpid = ""; + SequenceI ps = null; + if (parentseq != jmp.getTo() + && parentseq.getDatasetSequence() != jmp.getTo()) { - mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()), - jmp.getTo(), jds)); + // chaining dbref rather than a handshaking one + jmpid = seqHash(ps = jmp.getTo()); } else { - String jmpid = ""; - SequenceI ps = null; - if (parentseq != jmp.getTo() - && parentseq.getDatasetSequence() != jmp.getTo()) - { - // chaining dbref rather than a handshaking one - jmpid = seqHash(ps = jmp.getTo()); - } - else - { - jmpid = seqHash(ps = parentseq); - } - mpc.setDseqFor(jmpid); - if (!seqRefIds.containsKey(mpc.getDseqFor())) - { - jalview.bin.Cache.log.debug("creatign new DseqFor ID"); - seqRefIds.put(mpc.getDseqFor(), ps); - } - else - { - jalview.bin.Cache.log.debug("reusing DseqFor ID"); - } + jmpid = seqHash(ps = parentseq); + } + mpc.setDseqFor(jmpid); + if (!seqRefIds.containsKey(mpc.getDseqFor())) + { + jalview.bin.Cache.log.debug("creatign new DseqFor ID"); + seqRefIds.put(mpc.getDseqFor(), ps); } + else + { + jalview.bin.Cache.log.debug("reusing DseqFor ID"); + } + mp.setMappingChoice(mpc); } } @@ -2225,14 +2358,10 @@ public class Jalview2XML } if (seqRefIds == null) { - seqRefIds = new HashMap(); - } - if (frefedSequence == null) - { - frefedSequence = new Vector(); + initSeqRefs(); } - AlignFrame af = null, _af = null; + IdentityHashMap importedDatasets = new IdentityHashMap(); Map gatherToThisFrame = new HashMap(); final String file = jprovider.getFilename(); try @@ -2260,13 +2389,24 @@ public class Jalview2XML if (true) // !skipViewport(object)) { _af = loadFromObject(object, file, true, jprovider); - if (object.getJalviewModelSequence().getViewportCount() > 0) + if (_af != null + && object.getJalviewModelSequence().getViewportCount() > 0) { - af = _af; - if (af.viewport.isGatherViewsHere()) + if (af == null) + { + // store a reference to the first view + af = _af; + } + if (_af.viewport.isGatherViewsHere()) { - gatherToThisFrame.put(af.viewport.getSequenceSetId(), af); + // if this is a gathered view, keep its reference since + // after gathering views, only this frame will remain + af = _af; + gatherToThisFrame.put(_af.viewport.getSequenceSetId(), _af); } + // Save dataset to register mappings once all resolved + importedDatasets.put(af.viewport.getAlignment().getDataset(), + af.viewport.getAlignment().getDataset()); } } entryCount++; @@ -2322,11 +2462,6 @@ public class Jalview2XML e.printStackTrace(); } - if (Desktop.instance != null) - { - Desktop.instance.stopLoading(); - } - /* * Regather multiple views (with the same sequence set id) to the frame (if * any) that is flagged as the one to gather to, i.e. convert them to tabbed @@ -2340,11 +2475,24 @@ public class Jalview2XML } restoreSplitFrames(); - + for (AlignmentI ds : importedDatasets.keySet()) + { + if (ds.getCodonFrames() != null) + { + StructureSelectionManager.getStructureSelectionManager( + Desktop.instance).registerMappings(ds.getCodonFrames()); + } + } if (errorMessage != null) { reportErrors(); } + + if (Desktop.instance != null) + { + Desktop.instance.stopLoading(); + } + return af; } @@ -2513,14 +2661,16 @@ public class Jalview2XML * @param pdbId * @return */ - String loadPDBFile(jarInputStreamProvider jprovider, String pdbId) + String loadPDBFile(jarInputStreamProvider jprovider, String pdbId, + String origFile) { if (alreadyLoadedPDB.containsKey(pdbId)) { return alreadyLoadedPDB.get(pdbId).toString(); } - String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb"); + String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb", + origFile); if (tempFile != null) { alreadyLoadedPDB.put(pdbId, tempFile); @@ -2537,14 +2687,26 @@ public class Jalview2XML * @param prefix * a prefix for the temporary file name, must be at least three * characters long + * @param origFile + * null or original file - so new file can be given the same suffix + * as the old one * @return */ protected String copyJarEntry(jarInputStreamProvider jprovider, - String jarEntryName, String prefix) + String jarEntryName, String prefix, String origFile) { BufferedReader in = null; PrintWriter out = null; - + String suffix = ".tmp"; + if (origFile == null) + { + origFile = jarEntryName; + } + int sfpos = origFile.lastIndexOf("."); + if (sfpos > -1 && sfpos < (origFile.length() - 3)) + { + suffix = "." + origFile.substring(sfpos + 1); + } try { JarInputStream jin = jprovider.getJarInputStream(); @@ -2562,7 +2724,7 @@ public class Jalview2XML if (entry != null) { in = new BufferedReader(new InputStreamReader(jin, UTF_8)); - File outFile = File.createTempFile(prefix, ".tmp"); + File outFile = File.createTempFile(prefix, suffix); outFile.deleteOnExit(); out = new PrintWriter(new FileOutputStream(outFile)); String data; @@ -2650,36 +2812,69 @@ public class Jalview2XML // LOAD SEQUENCES List hiddenSeqs = null; - jalview.datamodel.Sequence jseq; + List tmpseqs = new ArrayList(); boolean multipleView = false; - + SequenceI referenceseqForView = null; JSeq[] jseqs = object.getJalviewModelSequence().getJSeq(); int vi = 0; // counter in vamsasSeq array for (int i = 0; i < jseqs.length; i++) { String seqId = jseqs[i].getId(); - if (seqRefIds.get(seqId) != null) + SequenceI tmpSeq = seqRefIds.get(seqId); + if (tmpSeq != null) { - tmpseqs.add(seqRefIds.get(seqId)); - multipleView = true; + if (!incompleteSeqs.containsKey(seqId)) + { + // may not need this check, but keep it for at least 2.9,1 release + if (tmpSeq.getStart()!=jseqs[i].getStart() || tmpSeq.getEnd()!=jseqs[i].getEnd()) + { + System.err + .println("Warning JAL-2154 regression: updating start/end for sequence " + + tmpSeq.toString() + " to " + jseqs[i]); + } + } else { + incompleteSeqs.remove(seqId); + } + if (vamsasSeq.length > vi && vamsasSeq[vi].getId().equals(seqId)) + { + // most likely we are reading a dataset XML document so + // update from vamsasSeq section of XML for this sequence + tmpSeq.setName(vamsasSeq[vi].getName()); + tmpSeq.setDescription(vamsasSeq[vi].getDescription()); + tmpSeq.setSequence(vamsasSeq[vi].getSequence()); + vi++; + } + else + { + // reading multiple views, so vamsasSeq set is a subset of JSeq + multipleView = true; + } + tmpSeq.setStart(jseqs[i].getStart()); + tmpSeq.setEnd(jseqs[i].getEnd()); + tmpseqs.add(tmpSeq); } else { - jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(), + tmpSeq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(), vamsasSeq[vi].getSequence()); - jseq.setDescription(vamsasSeq[vi].getDescription()); - jseq.setStart(jseqs[i].getStart()); - jseq.setEnd(jseqs[i].getEnd()); - jseq.setVamsasId(uniqueSetSuffix + seqId); - seqRefIds.put(vamsasSeq[vi].getId(), jseq); - tmpseqs.add(jseq); + tmpSeq.setDescription(vamsasSeq[vi].getDescription()); + tmpSeq.setStart(jseqs[i].getStart()); + tmpSeq.setEnd(jseqs[i].getEnd()); + tmpSeq.setVamsasId(uniqueSetSuffix + seqId); + seqRefIds.put(vamsasSeq[vi].getId(), tmpSeq); + tmpseqs.add(tmpSeq); vi++; } + if (jseqs[i].hasViewreference() && jseqs[i].getViewreference()) + { + referenceseqForView = tmpseqs.get(tmpseqs.size() - 1); + } + if (jseqs[i].getHidden()) { if (hiddenSeqs == null) @@ -2687,9 +2882,8 @@ public class Jalview2XML hiddenSeqs = new ArrayList(); } - hiddenSeqs.add(seqRefIds.get(seqId)); + hiddenSeqs.add(tmpSeq); } - } // / @@ -2698,31 +2892,51 @@ public class Jalview2XML SequenceI[] orderedSeqs = tmpseqs .toArray(new SequenceI[tmpseqs.size()]); - Alignment al = new Alignment(orderedSeqs); - - // / Add the alignment properties - for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++) - { - SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i); - al.setProperty(ssp.getKey(), ssp.getValue()); - } - - // / - // SequenceFeatures are added to the DatasetSequence, - // so we must create or recover the dataset before loading features + AlignmentI al = null; + // so we must create or recover the dataset alignment before going further // /////////////////////////////// if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "") { - // older jalview projects do not have a dataset id. + // older jalview projects do not have a dataset - so creat alignment and + // dataset + al = new Alignment(orderedSeqs); al.setDataset(null); } else { - // recover dataset - passing on flag indicating if this a 'viewless' - // sequence set (a.k.a. a stored dataset for the project) - recoverDatasetFor(vamsasSet, al, object.getJalviewModelSequence() - .getViewportCount() == 0); + boolean isdsal = object.getJalviewModelSequence().getViewportCount() == 0; + if (isdsal) + { + // we are importing a dataset record, so + // recover reference to an alignment already materialsed as dataset + al = getDatasetFor(vamsasSet.getDatasetId()); + } + if (al == null) + { + // materialse the alignment + al = new Alignment(orderedSeqs); + } + if (isdsal) + { + addDatasetRef(vamsasSet.getDatasetId(), al); + } + + // finally, verify all data in vamsasSet is actually present in al + // passing on flag indicating if it is actually a stored dataset + recoverDatasetFor(vamsasSet, al, isdsal); + } + + if (referenceseqForView != null) + { + al.setSeqrep(referenceseqForView); } + // / Add the alignment properties + for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++) + { + SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i); + al.setProperty(ssp.getKey(), ssp.getValue()); + } + // /////////////////////////////// Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this?? @@ -2730,6 +2944,12 @@ public class Jalview2XML { // load sequence features, database references and any associated PDB // structures for the alignment + // + // prior to 2.10, this part would only be executed the first time a + // sequence was encountered, but not afterwards. + // now, for 2.10 projects, this is also done if the xml doc includes + // dataset sequences not actually present in any particular view. + // for (int i = 0; i < vamsasSeq.length; i++) { if (jseqs[i].getFeaturesCount() > 0) @@ -2756,13 +2976,17 @@ public class Jalview2XML } } - - al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf); + // adds feature to datasequence's feature set (since Jalview 2.10) + al.getSequenceAt(i).addSequenceFeature(sf); } } if (vamsasSeq[i].getDBRefCount() > 0) { - addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]); + // adds dbrefs to datasequence's set (since Jalview 2.10) + addDBRefs( + al.getSequenceAt(i).getDatasetSequence() == null ? al.getSequenceAt(i) + : al.getSequenceAt(i).getDatasetSequence(), + vamsasSeq[i]); } if (jseqs[i].getPdbidsCount() > 0) { @@ -2773,9 +2997,9 @@ public class Jalview2XML entry.setId(ids[p].getId()); if (ids[p].getType() != null) { - if (ids[p].getType().equalsIgnoreCase("PDB")) + if (PDBEntry.Type.getType(ids[p].getType()) != null) { - entry.setType(PDBEntry.Type.PDB); + entry.setType(PDBEntry.Type.getType(ids[p].getType())); } else { @@ -2786,16 +3010,36 @@ public class Jalview2XML { if (!pdbloaded.containsKey(ids[p].getFile())) { - entry.setFile(loadPDBFile(jprovider, ids[p].getId())); + entry.setFile(loadPDBFile(jprovider, ids[p].getId(), + ids[p].getFile())); } else { entry.setFile(pdbloaded.get(ids[p].getId()).toString()); } } + if (ids[p].getPdbentryItem() != null) + { + entry.setProperty(new Hashtable()); + for (PdbentryItem item : ids[p].getPdbentryItem()) + { + for (Property pr : item.getProperty()) + { + entry.getProperty().put(pr.getName(), pr.getValue()); + } + } + } StructureSelectionManager.getStructureSelectionManager( Desktop.instance).registerPDBEntry(entry); - al.getSequenceAt(i).getDatasetSequence().addPDBId(entry); + // adds PDBEntry to datasequence's set (since Jalview 2.10) + if (al.getSequenceAt(i).getDatasetSequence() != null) + { + al.getSequenceAt(i).getDatasetSequence().addPDBId(entry); + } + else + { + al.getSequenceAt(i).addPDBId(entry); + } } } } @@ -2824,20 +3068,20 @@ public class Jalview2XML if (maps[m].getMapping() != null) { mapping = addMapping(maps[m].getMapping()); - } - if (dnaseq != null && mapping.getTo() != null) - { - cf.addMap(dnaseq, mapping.getTo(), mapping.getMap()); - } - else - { - // defer to later - frefedSequence.add(new Object[] { maps[m].getDnasq(), cf, - mapping }); + if (dnaseq != null && mapping.getTo() != null) + { + cf.addMap(dnaseq, mapping.getTo(), mapping.getMap()); + } + else + { + // defer to later + frefedSequence.add(newAlcodMapRef(maps[m].getDnasq(), cf, + mapping)); + } } } + al.addCodonFrame(cf); } - al.addCodonFrame(cf); } } @@ -3337,7 +3581,7 @@ public class Jalview2XML String rnaTitle = ss.getTitle(); String sessionState = ss.getViewerState(); String tempStateFile = copyJarEntry(jprovider, sessionState, - "varna"); + "varna", null); RnaModel rna = new RnaModel(rnaTitle, ann, seq, null, gapped); appVarna.addModelSession(rna, rnaTitle, tempStateFile); } @@ -3512,7 +3756,8 @@ public class Jalview2XML // Originally : ids[p].getFile() // : TODO: verify external PDB file recovery still works in normal // jalview project load - jpdb.setFile(loadPDBFile(jprovider, ids[p].getId())); + jpdb.setFile(loadPDBFile(jprovider, ids[p].getId(), + ids[p].getFile())); jpdb.setId(ids[p].getId()); int x = structureState.getXpos(); @@ -3523,7 +3768,8 @@ public class Jalview2XML // Probably don't need to do this anymore... // Desktop.desktop.getComponentAt(x, y); // TODO: NOW: check that this recovers the PDB file correctly. - String pdbFile = loadPDBFile(jprovider, ids[p].getId()); + String pdbFile = loadPDBFile(jprovider, ids[p].getId(), + ids[p].getFile()); jalview.datamodel.SequenceI seq = seqRefIds.get(jseqs[i] .getId() + ""); if (sviewid == null) @@ -3683,7 +3929,7 @@ public class Jalview2XML */ String viewerJarEntryName = getViewerJarEntryName(data.getViewId()); chimeraSessionFile = copyJarEntry(jprovider, viewerJarEntryName, - "chimera"); + "chimera", null); Set> fileData = data.getFileData() .entrySet(); @@ -3764,6 +4010,12 @@ public class Jalview2XML // filename // translation differently. StructureData filedat = oldFiles.get(new File(oldfilenam)); + if (filedat == null) + { + String reformatedOldFilename = oldfilenam.replaceAll("/", + "\\\\"); + filedat = oldFiles.get(new File(reformatedOldFilename)); + } newFileLoc.append(Platform.escapeString(filedat.getFilePath())); pdbfilenames.add(filedat.getFilePath()); pdbids.add(filedat.getPdbId()); @@ -4022,18 +4274,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")) { @@ -4044,45 +4300,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 newStructureViewers = null; @@ -4110,7 +4329,7 @@ public class Jalview2XML } AlignFrame loadViewport(String file, JSeq[] JSEQ, - List hiddenSeqs, Alignment al, + List hiddenSeqs, AlignmentI al, JalviewModelSequence jms, Viewport view, String uniqueSeqSetId, String viewId, List autoAlan) { @@ -4126,6 +4345,12 @@ public class Jalview2XML .getSequenceAt(i), new Colour(JSEQ[i].getColour())); } + if (al.hasSeqrep()) + { + af.getViewport().setColourByReferenceSeq(true); + af.getViewport().setDisplayReferenceSeq(true); + } + af.viewport.setGatherViewsHere(view.getGatheredViews()); if (view.getSequenceSetId() != null) @@ -4152,25 +4377,25 @@ public class Jalview2XML { for (int s = 0; s < JSEQ.length; s++) { - jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup(); - + SequenceGroup hidden = new SequenceGroup(); + boolean isRepresentative = false; for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++) { - hidden.addSequence( - al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false); + isRepresentative = true; + SequenceI sequenceToHide = al.getSequenceAt(JSEQ[s] + .getHiddenSequences(r)); + hidden.addSequence(sequenceToHide, false); + // remove from hiddenSeqs list so we don't try to hide it twice + hiddenSeqs.remove(sequenceToHide); + } + if (isRepresentative) + { + SequenceI representativeSequence = al.getSequenceAt(s); + hidden.addSequence(representativeSequence, false); + af.viewport.hideRepSequences(representativeSequence, hidden); } - af.viewport.hideRepSequences(al.getSequenceAt(s), hidden); } - // jalview.datamodel.SequenceI[] hseqs = new - // jalview.datamodel.SequenceI[hiddenSeqs - // .size()]; - // - // for (int s = 0; s < hiddenSeqs.size(); s++) - // { - // hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s); - // } - SequenceI[] hseqs = hiddenSeqs.toArray(new SequenceI[hiddenSeqs .size()]); af.viewport.hideSequence(hseqs); @@ -4433,7 +4658,7 @@ public class Jalview2XML } } af.setMenusFromViewport(af.viewport); - + af.setTitle(view.getTitle()); // TODO: we don't need to do this if the viewport is aready visible. /* * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it @@ -4458,7 +4683,7 @@ public class Jalview2XML } private ColourSchemeI constructAnnotationColour( - AnnotationColours viewAnnColour, AlignFrame af, Alignment al, + AnnotationColours viewAnnColour, AlignFrame af, AlignmentI al, JalviewModelSequence jms, boolean checkGroupAnnColour) { boolean propagateAnnColour = false; @@ -4582,7 +4807,7 @@ public class Jalview2XML return cs; } - private void reorderAutoannotation(AlignFrame af, Alignment al, + private void reorderAutoannotation(AlignFrame af, AlignmentI al, List autoAlan) { // copy over visualization settings for autocalculated annotation in the @@ -4737,10 +4962,11 @@ public class Jalview2XML } } - private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al, + private void recoverDatasetFor(SequenceSet vamsasSet, AlignmentI al, boolean ignoreUnrefed) { - jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId()); + jalview.datamodel.AlignmentI ds = getDatasetFor(vamsasSet + .getDatasetId()); Vector dseqs = null; if (ds == null) { @@ -4750,7 +4976,7 @@ public class Jalview2XML for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++) { Sequence vamsasSeq = vamsasSet.getSequence(i); - ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed); + ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed, i); } // create a new dataset if (ds == null) @@ -4777,18 +5003,29 @@ public class Jalview2XML * dataset alignment * @param dseqs * vector to add new dataset sequence to + * @param ignoreUnrefed + * - when true, don't create new sequences from vamsasSeq if it's id + * doesn't already have an asssociated Jalview sequence. + * @param vseqpos + * - used to reorder the sequence in the alignment according to the + * vamsasSeq array ordering, to preserve ordering of dataset */ private void ensureJalviewDatasetSequence(Sequence vamsasSeq, - AlignmentI ds, Vector dseqs, boolean ignoreUnrefed) + AlignmentI ds, Vector dseqs, boolean ignoreUnrefed, int vseqpos) { // JBP TODO: Check this is called for AlCodonFrames to support recovery of // xRef Codon Maps SequenceI sq = seqRefIds.get(vamsasSeq.getId()); + boolean reorder = false; SequenceI dsq = null; if (sq != null && sq.getDatasetSequence() != null) { dsq = sq.getDatasetSequence(); } + else + { + reorder = true; + } if (sq == null && ignoreUnrefed) { return; @@ -4884,21 +5121,50 @@ public class Jalview2XML // + (post ? "appended" : "")); } } + else + { + // sequence refs are identical. We may need to update the existing dataset + // alignment with this one, though. + if (ds != null && dseqs == null) + { + int opos = ds.findIndex(dsq); + SequenceI tseq = null; + if (opos != -1 && vseqpos != opos) + { + // remove from old position + ds.deleteSequence(dsq); + } + if (vseqpos < ds.getHeight()) + { + if (vseqpos != opos) + { + // save sequence at destination position + tseq = ds.getSequenceAt(vseqpos); + ds.replaceSequenceAt(vseqpos, dsq); + ds.addSequence(tseq); + } + } + else + { + ds.addSequence(dsq); + } + } + } } /* * TODO use AlignmentI here and in related methods - needs * AlignmentI.getDataset() changed to return AlignmentI instead of Alignment */ - Hashtable datasetIds = null; + Hashtable datasetIds = null; - IdentityHashMap dataset2Ids = null; + IdentityHashMap dataset2Ids = null; - private Alignment getDatasetFor(String datasetId) + private AlignmentI getDatasetFor(String datasetId) { if (datasetIds == null) { - datasetIds = new Hashtable(); + datasetIds = new Hashtable(); return null; } if (datasetIds.containsKey(datasetId)) @@ -4908,11 +5174,11 @@ public class Jalview2XML return null; } - private void addDatasetRef(String datasetId, Alignment dataset) + private void addDatasetRef(String datasetId, AlignmentI dataset) { if (datasetIds == null) { - datasetIds = new Hashtable(); + datasetIds = new Hashtable(); } datasetIds.put(datasetId, dataset); } @@ -4923,7 +5189,7 @@ public class Jalview2XML * @param dataset * @return */ - private String getDatasetIdRef(Alignment dataset) + private String getDatasetIdRef(AlignmentI dataset) { if (dataset.getDataset() != null) { @@ -4935,7 +5201,7 @@ public class Jalview2XML // make a new datasetId and record it if (dataset2Ids == null) { - dataset2Ids = new IdentityHashMap(); + dataset2Ids = new IdentityHashMap(); } else { @@ -5003,7 +5269,7 @@ public class Jalview2XML } else { - frefedSequence.add(new Object[] { dsfor, jmap }); + frefedSequence.add(newMappingRef(dsfor, jmap)); } } else @@ -5041,6 +5307,7 @@ public class Jalview2XML djs.setEnd(jmap.getMap().getToHighest()); djs.setVamsasId(uniqueSetSuffix + sqid); jmap.setTo(djs); + incompleteSeqs.put(sqid, djs); seqRefIds.put(sqid, djs); } diff --git a/src/jalview/gui/JalviewDialog.java b/src/jalview/gui/JalviewDialog.java index 3a4dfab..8742253 100644 --- a/src/jalview/gui/JalviewDialog.java +++ b/src/jalview/gui/JalviewDialog.java @@ -59,6 +59,7 @@ public abstract class JalviewDialog extends JPanel new Thread(new Runnable() { + @Override public void run() { frame.setVisible(true); @@ -95,6 +96,7 @@ public abstract class JalviewDialog extends JPanel ok.setText(MessageManager.getString("action.ok")); ok.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { okPressed(); @@ -105,6 +107,7 @@ public abstract class JalviewDialog extends JPanel cancel.setText(MessageManager.getString("action.cancel")); cancel.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { cancelPressed(); diff --git a/src/jalview/gui/OptsAndParamsPage.java b/src/jalview/gui/OptsAndParamsPage.java index 801cb1f..040a1e5 100644 --- a/src/jalview/gui/OptsAndParamsPage.java +++ b/src/jalview/gui/OptsAndParamsPage.java @@ -114,7 +114,7 @@ public class OptsAndParamsPage .wrapTooltip( true, ((desc == null || desc.trim().length() == 0) ? MessageManager - .getString("label.opt_and_params_further_details ") + .getString("label.opt_and_params_further_details") : desc) + "
        ")); enabled.addMouseListener(this); @@ -210,7 +210,7 @@ public class OptsAndParamsPage @Override public void mouseClicked(MouseEvent e) { - if (e.isPopupTrigger()) + if (e.isPopupTrigger()) // for Windows { showUrlPopUp(this, finfo.toString(), e.getX(), e.getY()); } @@ -233,15 +233,15 @@ public class OptsAndParamsPage @Override public void mousePressed(MouseEvent e) { - // TODO Auto-generated method stub - + if (e.isPopupTrigger()) // Mac + { + showUrlPopUp(this, finfo.toString(), e.getX(), e.getY()); + } } @Override public void mouseReleased(MouseEvent e) { - // TODO Auto-generated method stub - } public void resetToDefault(boolean setDefaultParams) @@ -357,7 +357,7 @@ public class OptsAndParamsPage + linkImageURL + "\"/>" + MessageManager - .getString("label.opt_and_params_further_detail") + .getString("label.opt_and_params_further_details") : ""))); } @@ -537,7 +537,7 @@ public class OptsAndParamsPage @Override public void mouseClicked(MouseEvent e) { - if (e.isPopupTrigger()) + if (e.isPopupTrigger()) // for Windows { showUrlPopUp(this, finfo.toString(), e.getX(), e.getY()); } @@ -560,8 +560,10 @@ public class OptsAndParamsPage @Override public void mousePressed(MouseEvent e) { - // TODO Auto-generated method stub - + if (e.isPopupTrigger()) // for Mac + { + showUrlPopUp(this, finfo.toString(), e.getX(), e.getY()); + } } @Override diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index de0dbe5..d09c756 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -295,14 +295,25 @@ public class OverviewPanel extends JPanel implements Runnable final boolean hasHiddenRows = av.hasHiddenRows(), hasHiddenCols = av .hasHiddenColumns(); boolean hiddenRow = false; + // get hidden row and hidden column map once at beginning. + // clone featureRenderer settings to avoid race conditions... if state is + // updated just need to refresh again for (row = 0; row < sequencesHeight; row++) { + if (resizeAgain) + { + break; + } if ((int) (row * sampleRow) == lastrow) { // No need to recalculate the colours, // Just copy from the row above for (col = 0; col < width; col++) { + if (resizeAgain) + { + break; + } miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1)); } continue; @@ -340,6 +351,10 @@ public class OverviewPanel extends JPanel implements Runnable for (col = 0; col < width; col++) { + if (resizeAgain) + { + break; + } if ((int) (col * sampleCol) == lastcol && (int) (row * sampleRow) == lastrow) { @@ -380,6 +395,10 @@ public class OverviewPanel extends JPanel implements Runnable renderer.updateFromAlignViewport(av); for (col = 0; col < width; col++) { + if (resizeAgain) + { + break; + } lastcol = (int) (col * sampleCol); { mg.translate(col, sequencesHeight); @@ -395,13 +414,17 @@ public class OverviewPanel extends JPanel implements Runnable resizing = false; - setBoxPosition(); - if (resizeAgain) { resizeAgain = false; updateOverviewImage(); } + else + { + lastMiniMe = miniMe; + } + + setBoxPosition(); } /** @@ -456,6 +479,7 @@ public class OverviewPanel extends JPanel implements Runnable repaint(); } + private BufferedImage lastMiniMe = null; /** * DOCUMENT ME! * @@ -465,19 +489,32 @@ public class OverviewPanel extends JPanel implements Runnable @Override public void paintComponent(Graphics g) { - if (resizing) + if (resizing || resizeAgain) { - g.setColor(Color.white); + if (lastMiniMe == null) + { + g.setColor(Color.white); + g.fillRect(0, 0, getWidth(), getHeight()); + } + else + { + g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); + } + g.setColor(new Color(100, 100, 100, 25)); g.fillRect(0, 0, getWidth(), getHeight()); } - else if (miniMe != null) + else if (lastMiniMe != null) { - g.drawImage(miniMe, 0, 0, this); + g.drawImage(lastMiniMe, 0, 0, this); + if (lastMiniMe != miniMe) + { + g.setColor(new Color(100, 100, 100, 25)); + g.fillRect(0, 0, getWidth(), getHeight()); + } } - + // TODO: render selected regions g.setColor(Color.red); g.drawRect(boxX, boxY, boxWidth, boxHeight); g.drawRect(boxX + 1, boxY + 1, boxWidth - 2, boxHeight - 2); - } } diff --git a/src/jalview/gui/PCAPanel.java b/src/jalview/gui/PCAPanel.java index 47add28..51d247d 100644 --- a/src/jalview/gui/PCAPanel.java +++ b/src/jalview/gui/PCAPanel.java @@ -21,6 +21,7 @@ package jalview.gui; import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.datamodel.ColumnSelection; import jalview.datamodel.SeqCigar; @@ -139,8 +140,8 @@ public class PCAPanel extends GPCAPanel implements Runnable, { // create an entry for this score matrix for use in PCA JCheckBoxMenuItem jm = new JCheckBoxMenuItem(); - jm.setText(MessageManager - .getStringOrReturn("label.score_model", sm)); + jm.setText(MessageManager.getStringOrReturn("label.score_model_", + sm)); jm.setSelected(pcaModel.getScore_matrix().equals(sm)); if ((ResidueProperties.scoreMatrices.get(sm).isDNA() && ResidueProperties.scoreMatrices .get(sm).isProtein()) @@ -383,8 +384,8 @@ public class PCAPanel extends GPCAPanel implements Runnable, { // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]); - Alignment al = new Alignment((SequenceI[]) alAndColsel[0]); - Alignment dataset = (av != null && av.getAlignment() != null) ? av + AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]); + AlignmentI dataset = (av != null && av.getAlignment() != null) ? av .getAlignment().getDataset() : null; if (dataset != null) { diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 1e0772a..d28dc60 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -91,8 +91,6 @@ import javax.swing.JRadioButtonMenuItem; */ public class PopupMenu extends JPopupMenu { - private static final String ALL_ANNOTATIONS = "All"; - JMenu groupMenu = new JMenu(); JMenuItem groupName = new JMenuItem(); @@ -478,8 +476,6 @@ public class PopupMenu extends JPopupMenu if (sg != null && sg.getSize() > 0) { - groupName.setText(MessageManager.formatMessage("label.name_param", - new Object[] { sg.getName() })); groupName.setText(MessageManager .getString("label.edit_name_and_description_current_group")); @@ -756,7 +752,8 @@ public class PopupMenu extends JPopupMenu showMenu.removeAll(); hideMenu.removeAll(); - final List all = Arrays.asList(ALL_ANNOTATIONS); + final List all = Arrays.asList(new String[] { MessageManager + .getString("label.all") }); addAnnotationTypeToShowHide(showMenu, forSequences, "", all, true, true); addAnnotationTypeToShowHide(hideMenu, forSequences, "", all, true, false); @@ -1098,7 +1095,6 @@ public class PopupMenu extends JPopupMenu */ private void jbInit() throws Exception { - groupMenu.setText(MessageManager.getString("label.group")); groupMenu.setText(MessageManager.getString("label.selection")); groupName.setText(MessageManager.getString("label.name")); groupName.addActionListener(new java.awt.event.ActionListener() @@ -2270,28 +2266,7 @@ public class PopupMenu extends JPopupMenu void hideSequences(boolean representGroup) { - SequenceGroup sg = ap.av.getSelectionGroup(); - if (sg == null || sg.getSize() < 1) - { - ap.av.hideSequence(new SequenceI[] { sequence }); - return; - } - - ap.av.setSelectionGroup(null); - - if (representGroup) - { - ap.av.hideRepSequences(sequence, sg); - - return; - } - - int gsize = sg.getSize(); - SequenceI[] hseqs = sg.getSequences().toArray(new SequenceI[gsize]); - - ap.av.hideSequence(hseqs); - // refresh(); TODO: ? needed ? - ap.av.sendSelection(); + ap.av.hideSequences(sequence, representGroup); } public void copy_actionPerformed() @@ -2363,45 +2338,6 @@ public class PopupMenu extends JPopupMenu ap, true)); } - public void enterPDB_actionPerformed() - { - String id = JOptionPane.showInternalInputDialog(Desktop.desktop, - MessageManager.getString("label.enter_pdb_id"), - MessageManager.getString("label.enter_pdb_id"), - JOptionPane.QUESTION_MESSAGE); - - if (id != null && id.length() > 0) - { - PDBEntry entry = new PDBEntry(); - entry.setId(id.toUpperCase()); - sequence.getDatasetSequence().addPDBId(entry); - } - } - - public void discoverPDB_actionPerformed() - { - - final SequenceI[] sequences = ((ap.av.getSelectionGroup() == null) ? new SequenceI[] - { sequence } - : ap.av.getSequenceSelection()); - Thread discpdb = new Thread(new Runnable() - { - @Override - public void run() - { - boolean isNuclueotide = ap.alignFrame.getViewport().getAlignment() - .isNucleotide(); - - new jalview.ws.DBRefFetcher(sequences, ap.alignFrame, null, - ap.alignFrame.featureSettings, isNuclueotide) - .fetchDBRefs(false); - - } - - }); - discpdb.start(); - } - public void sequenceFeature_actionPerformed() { SequenceGroup sg = ap.av.getSelectionGroup(); diff --git a/src/jalview/gui/Preferences.java b/src/jalview/gui/Preferences.java index 6b2d3c4..afc93e0 100755 --- a/src/jalview/gui/Preferences.java +++ b/src/jalview/gui/Preferences.java @@ -164,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. */ @@ -359,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()); @@ -515,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 @@ -983,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(); + } + } } diff --git a/src/jalview/gui/RedundancyPanel.java b/src/jalview/gui/RedundancyPanel.java index 7fb0593..a9d2690 100755 --- a/src/jalview/gui/RedundancyPanel.java +++ b/src/jalview/gui/RedundancyPanel.java @@ -172,7 +172,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable progress = null; label.setText(MessageManager - .getString("label.enter_redundancy_thereshold")); + .getString("label.enter_redundancy_threshold")); slider.setVisible(true); applyButton.setEnabled(true); valueField.setVisible(true); diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java index aedb157..0aa2459 100755 --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@ -23,7 +23,10 @@ package jalview.gui; import jalview.datamodel.ColumnSelection; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; +import jalview.renderer.ScaleRenderer; +import jalview.renderer.ScaleRenderer.ScaleMark; import jalview.util.MessageManager; +import jalview.util.Platform; import java.awt.Color; import java.awt.FontMetrics; @@ -35,24 +38,23 @@ 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; @@ -61,13 +63,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; @@ -106,10 +121,19 @@ public class ScalePanel extends JPanel implements MouseMotionListener, min = res; max = res; - if (evt.isPopupTrigger()) + if (evt.isPopupTrigger()) // Mac: mousePressed { rightMouseButtonPressed(evt, res); } + else if (SwingUtilities.isRightMouseButton(evt) && !Platform.isAMac()) + { + /* + * defer right-mouse click handling to mouse up on Windows + * (where isPopupTrigger() will answer true) + * but accept Cmd-click on Mac which passes isRightMouseButton + */ + return; + } else { leftMouseButtonPressed(evt, res); @@ -208,7 +232,12 @@ public class ScalePanel extends JPanel implements MouseMotionListener, */ protected void leftMouseButtonPressed(MouseEvent evt, final int res) { - if (!evt.isControlDown() && !evt.isShiftDown()) + /* + * Ctrl-click/Cmd-click adds to the selection + * Shift-click extends the selection + */ + // TODO Problem: right-click on Windows not reported until mouseReleased?!? + if (!Platform.isControlDown(evt) && !evt.isShiftDown()) { av.getColumnSelection().clear(); } @@ -267,8 +296,14 @@ public class ScalePanel extends JPanel implements MouseMotionListener, if (!stretchingGroup) { - ap.paintAlignment(false); - + if (evt.isPopupTrigger()) // Windows: mouseReleased + { + rightMouseButtonPressed(evt, res); + } + else + { + ap.paintAlignment(false); + } return; } @@ -392,6 +427,8 @@ public class ScalePanel extends JPanel implements MouseMotionListener, @Override public void mouseMoved(MouseEvent evt) { + this.setToolTipText(null); + reveal = null; if (!av.hasHiddenColumns()) { return; @@ -401,7 +438,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()) @@ -414,17 +450,11 @@ public class ScalePanel extends JPanel implements MouseMotionListener, .getString("label.reveal_hidden_columns")); break; } - else - { - this.setToolTipText(null); - } } } repaint(); } - int[] reveal; - /** * DOCUMENT ME! * @@ -459,7 +489,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)); @@ -488,92 +517,16 @@ public class ScalePanel extends JPanel implements MouseMotionListener, } } } - // Draw the scale numbers - gg.setColor(Color.black); - - int scalestartx = (startx / 10) * 10; - - SequenceI refSeq = av.getAlignment().getSeqrep(); - int refSp = 0, refEp = -1, refStart = 0, refEnd = -1, 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]; - refEp = refbounds[1]; - refStart = refbounds[2]; - refEnd = refbounds[3]; - refStartI = refbounds[4]; - refEndI = refbounds[5]; - scalestartx = refSp + ((scalestartx - refSp) / 10) * 10; - } - int widthx = 1 + endx - startx; FontMetrics fm = gg.getFontMetrics(av.getFont()); - int y = avCharHeight - fm.getDescent(); - - if (refSeq == null && scalestartx % 10 == 0) - { - scalestartx += 5; - } - - String string; - int maxX = 0, refN, iadj; - // todo: add a 'reference origin column' to set column number relative to - for (int i = scalestartx; i < endx; i += 5) - { - if (((i - refSp) % 10) == 0) - { - iadj = av.getColumnSelection().adjustForHiddenColumns(i) - 1; - if (refSeq == null) - { - string = String.valueOf(iadj + 1); - } - else - { - 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); - } - } - 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; + int yOf = fm.getDescent(); + y -= yOf; if (av.hasHiddenColumns()) { + // draw any hidden column markers gg.setColor(Color.blue); int res; if (av.getShowHiddenMarkers() @@ -590,20 +543,44 @@ 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); - if (reveal != null && reveal[0] > startx && reveal[0] < endx) + int maxX = 0; + List marks = new ScaleRenderer().calculateMarks(av, startx, + endx); + + for (ScaleMark mark : marks) + { + boolean major = mark.major; + int mpos = mark.column; // (i - startx - 1) + String mstring = mark.text; + if (mstring != null) { - gg.drawString(MessageManager.getString("label.reveal_columns"), - reveal[0] * avCharWidth, 0); + 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)); } } - } + } diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 8a120c5..1355b9b 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -24,6 +24,8 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.SearchResults; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; +import jalview.renderer.ScaleRenderer; +import jalview.renderer.ScaleRenderer.ScaleMark; import jalview.util.ColorUtils; import java.awt.BasicStroke; @@ -123,24 +125,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 (ScaleMark mark : new ScaleRenderer().calculateMarks(av, startx, + endx)) { - int value = i; - if (av.hasHiddenColumns()) + int mpos = mark.column; // (i - startx - 1) + if (mpos < 0) { - value = av.getColumnSelection().adjustForHiddenColumns(value); + continue; } + String mstring = mark.text; - 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 (mark.major) + { + 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); + } } } diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index 3fbb809..136d222 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -43,6 +43,7 @@ import jalview.structure.VamsasSource; import jalview.util.Comparison; import jalview.util.MappingUtils; import jalview.util.MessageManager; +import jalview.util.Platform; import jalview.viewmodel.AlignmentViewport; import java.awt.BorderLayout; @@ -582,6 +583,13 @@ public class SeqPanel extends JPanel implements MouseListener, mouseDragging = false; mouseWheelPressed = false; + if (evt.isPopupTrigger()) // Windows: mouseReleased + { + showPopupMenu(evt); + evt.consume(); + return; + } + if (!editingSeqs) { doMouseReleasedDefineMode(evt); @@ -825,6 +833,21 @@ public class SeqPanel extends JPanel implements MouseListener, String lastTooltip; /** + * set when the current UI interaction has resulted in a change that requires + * overview shading to be recalculated. this could be changed to something + * more expressive that indicates what actually has changed, so selective + * redraws can be applied + */ + private boolean needOverviewUpdate = false; // TODO: refactor to avcontroller + + /** + * set if av.getSelectionGroup() refers to a group that is defined on the + * alignment view, rather than a transient selection + */ + // private boolean editingDefinedGroup = false; // TODO: refactor to + // avcontroller or viewModel + + /** * Set status message in alignment panel * * @param sequence @@ -843,7 +866,8 @@ public class SeqPanel extends JPanel implements MouseListener, * Sequence number (if known), and sequence name. */ String seqno = seq == -1 ? "" : " " + (seq + 1); - text.append("Sequence" + seqno + " ID: " + sequence.getName()); + text.append("Sequence").append(seqno).append(" ID: ") + .append(sequence.getName()); String residue = null; /* @@ -1531,9 +1555,10 @@ public class SeqPanel extends JPanel implements MouseListener, */ public void doMousePressedDefineMode(MouseEvent evt) { - int res = findRes(evt); - int seq = findSeq(evt); + final int res = findRes(evt); + final int seq = findSeq(evt); oldSeq = seq; + needOverviewUpdate = false; startWrapBlock = wrappedBlock; @@ -1596,28 +1621,21 @@ public class SeqPanel extends JPanel implements MouseListener, } av.setSelectionGroup(stretchGroup); - } - if (evt.isPopupTrigger()) + if (evt.isPopupTrigger()) // Mac: mousePressed { - List allFeatures = ap.getFeatureRenderer() - .findFeaturesAtRes(sequence.getDatasetSequence(), - sequence.findPosition(res)); - List links = new ArrayList(); - for (SequenceFeature sf : allFeatures) - { - if (sf.links != null) - { - for (String link : sf.links) - { - links.add(link); - } - } - } + showPopupMenu(evt); + return; + } - PopupMenu pop = new PopupMenu(ap, null, links); - pop.show(this, evt.getX(), evt.getY()); + /* + * defer right-mouse click handling to mouseReleased on Windows + * (where isPopupTrigger() will answer true) + * NB isRightMouseButton is also true for Cmd-click on Mac + */ + if (SwingUtilities.isRightMouseButton(evt) && !Platform.isAMac()) + { return; } @@ -1639,7 +1657,6 @@ public class SeqPanel extends JPanel implements MouseListener, sg.setEndRes(res); sg.addSequence(sequence, false); av.setSelectionGroup(sg); - stretchGroup = sg; if (av.getConservationSelected()) @@ -1671,6 +1688,37 @@ public class SeqPanel extends JPanel implements MouseListener, } /** + * Build and show a pop-up menu at the right-click mouse position + * + * @param evt + * @param res + * @param sequence + */ + void showPopupMenu(MouseEvent evt) + { + final int res = findRes(evt); + final int seq = findSeq(evt); + SequenceI sequence = av.getAlignment().getSequenceAt(seq); + List allFeatures = ap.getFeatureRenderer() + .findFeaturesAtRes(sequence.getDatasetSequence(), + sequence.findPosition(res)); + List links = new ArrayList(); + for (SequenceFeature sf : allFeatures) + { + if (sf.links != null) + { + for (String link : sf.links) + { + links.add(link); + } + } + } + + PopupMenu pop = new PopupMenu(ap, null, links); + pop.show(this, evt.getX(), evt.getY()); + } + + /** * DOCUMENT ME! * * @param evt @@ -1682,9 +1730,10 @@ public class SeqPanel extends JPanel implements MouseListener, { return; } - - stretchGroup.recalcConservation(); // always do this - annotation has own - // state + // always do this - annotation has own state + // but defer colourscheme update until hidden sequences are passed in + boolean vischange = stretchGroup.recalcConservation(true); + needOverviewUpdate |= vischange && av.isSelectionDefinedGroup(); if (stretchGroup.cs != null) { stretchGroup.cs.alignmentChanged(stretchGroup, @@ -1702,8 +1751,8 @@ public class SeqPanel extends JPanel implements MouseListener, } } PaintRefresher.Refresh(this, av.getSequenceSetId()); - ap.paintAlignment(true); - + ap.paintAlignment(needOverviewUpdate); + needOverviewUpdate =false; changeEndRes = false; changeStartRes = false; stretchGroup = null; @@ -1757,6 +1806,7 @@ public class SeqPanel extends JPanel implements MouseListener, if (res > (stretchGroup.getStartRes() - 1)) { stretchGroup.setEndRes(res); + needOverviewUpdate |= av.isSelectionDefinedGroup(); } } else if (changeStartRes) @@ -1764,6 +1814,7 @@ public class SeqPanel extends JPanel implements MouseListener, if (res < (stretchGroup.getEndRes() + 1)) { stretchGroup.setStartRes(res); + needOverviewUpdate |= av.isSelectionDefinedGroup(); } } @@ -1797,6 +1848,7 @@ public class SeqPanel extends JPanel implements MouseListener, if (stretchGroup.getSequences(null).contains(nextSeq)) { stretchGroup.deleteSequence(seq, false); + needOverviewUpdate |= av.isSelectionDefinedGroup(); } else { @@ -1806,6 +1858,7 @@ public class SeqPanel extends JPanel implements MouseListener, } stretchGroup.addSequence(nextSeq, false); + needOverviewUpdate |= av.isSelectionDefinedGroup(); } } @@ -1947,10 +2000,12 @@ public class SeqPanel extends JPanel implements MouseListener, // do we want to thread this ? (contention with seqsel and colsel locks, I // suspect) - // rules are: colsel is copied if there is a real intersection between - // sequence selection + /* + * only copy colsel if there is a real intersection between + * sequence selection and this panel's alignment + */ boolean repaint = false; - boolean copycolsel = true; + boolean copycolsel = false; SequenceGroup sgroup = null; if (seqsel != null && seqsel.getSize() > 0) @@ -1964,11 +2019,9 @@ public class SeqPanel extends JPanel implements MouseListener, } sgroup = seqsel.intersect(av.getAlignment(), (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null); - if ((sgroup == null || sgroup.getSize() == 0) - || (colsel == null || colsel.isEmpty())) + if ((sgroup != null && sgroup.getSize() > 0)) { - // don't copy columns if the region didn't intersect. - copycolsel = false; + copycolsel = true; } } if (sgroup != null && sgroup.getSize() > 0) @@ -2061,7 +2114,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()); diff --git a/src/jalview/gui/SequenceFetcher.java b/src/jalview/gui/SequenceFetcher.java index e567d20..5d4ea68 100755 --- a/src/jalview/gui/SequenceFetcher.java +++ b/src/jalview/gui/SequenceFetcher.java @@ -31,6 +31,7 @@ import jalview.fts.service.uniprot.UniprotFTSPanel; import jalview.io.gff.SequenceOntologyI; import jalview.util.DBRefUtils; import jalview.util.MessageManager; +import jalview.util.Platform; import jalview.ws.dbsources.das.api.DasSourceRegistryI; import jalview.ws.seqfetcher.DbSourceProxy; @@ -42,6 +43,7 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -114,8 +116,6 @@ public class SequenceFetcher extends JPanel implements Runnable private static Thread initingThread = null; - int debounceTrap = 0; - public JTextArea getTextArea() { return textArea; @@ -201,8 +201,19 @@ public class SequenceFetcher extends JPanel implements Runnable private IProgressIndicator progressIndicator; + private volatile boolean _isConstructing = false; + + private List newAlframes = null; + public SequenceFetcher(IProgressIndicator guiIndic) { + this(guiIndic, null, null); + } + + public SequenceFetcher(IProgressIndicator guiIndic, + final String selectedDb, final String queryString) + { + this._isConstructing=true; this.progressIndicator = guiIndic; final SequenceFetcher us = this; // launch initialiser thread @@ -214,7 +225,8 @@ public class SequenceFetcher extends JPanel implements Runnable { if (getSequenceFetcherSingleton(progressIndicator) != null) { - us.initGui(progressIndicator); + us.initGui(progressIndicator, selectedDb, queryString); + us._isConstructing=false; } else { @@ -240,6 +252,26 @@ public class SequenceFetcher extends JPanel implements Runnable }); sf.start(); } + /** + * blocking call which creates a new sequence fetcher panel, configures it and presses the OK button with the given database and query. + * @param database + * @param query + */ + public static List fetchAndShow(String database, String query) + { + final SequenceFetcher sf = new SequenceFetcher(Desktop.instance, database, query); + while (sf._isConstructing) + { + try { Thread.sleep(50); + } catch (Exception q) + { + return Collections.emptyList(); + } + } + sf.newAlframes = new ArrayList(); + sf.run(); + return sf.newAlframes; + } private class DatabaseAuthority extends DefaultMutableTreeNode { @@ -250,13 +282,59 @@ public class SequenceFetcher extends JPanel implements Runnable { }; + + /** + * initialise the database and query for this fetcher panel + * + * @param selectedDb + * - string that should correspond to a sequence fetcher + * @param queryString + * - string that will be entered in the query dialog + * @return true if UI was configured with valid database and query string + */ + protected boolean setInitialQuery(String selectedDb, String queryString) + { + if (selectedDb == null || selectedDb.trim().length() == 0) + { + return false; + } + try + { + List sp = sfetch.getSourceProxy(selectedDb); + for (DbSourceProxy sourcep : sp) + { + if (sourcep.getTier() == 0) + { + database.selection = Arrays + .asList(new DbSourceProxy[] { sourcep }); + break; + } + } + if (database.selection == null || database.selection.size() == 0) + { + System.err.println("Ignoring fetch parameter db='" + selectedDb + + "'"); + return false; + } + textArea.setText(queryString); + } catch (Exception q) + { + System.err.println("Ignoring fetch parameter db='" + selectedDb + + "' and query='" + queryString + "'"); + return false; + } + return true; + } /** * called by thread spawned by constructor * * @param guiWindow + * @param queryString + * @param selectedDb */ - private void initGui(IProgressIndicator guiWindow) + private void initGui(IProgressIndicator guiWindow, String selectedDb, + String queryString) { this.guiWindow = guiWindow; if (guiWindow instanceof AlignFrame) @@ -267,6 +345,16 @@ public class SequenceFetcher extends JPanel implements Runnable try { jbInit(); + /* + * configure the UI with any query parameters we were called with + */ + if (!setInitialQuery(selectedDb, queryString)) + { + /* + * none provided, so show the database chooser + */ + database.waitForInput(); + } } catch (Exception ex) { ex.printStackTrace(); @@ -274,13 +362,13 @@ public class SequenceFetcher extends JPanel implements Runnable frame = new JInternalFrame(); frame.setContentPane(this); - if (new jalview.util.Platform().isAMac()) + if (Platform.isAMac()) { - Desktop.addInternalFrame(frame, getFrameTitle(), 400, 240); + Desktop.addInternalFrame(frame, getFrameTitle(), false, 400, 240); } else { - Desktop.addInternalFrame(frame, getFrameTitle(), 400, 180); + Desktop.addInternalFrame(frame, getFrameTitle(), false, 400, 180); } } @@ -363,25 +451,39 @@ public class SequenceFetcher extends JPanel implements Runnable jPanel1.add(example); jPanel1.add(clear); jPanel1.add(close); - jPanel3.add(jPanel2, java.awt.BorderLayout.CENTER); jPanel2.setLayout(borderLayout3); - databaseButt = database.getDatabaseSelectorButton(); + databaseButt = /*database.getDatabaseSelectorButton(); + final JButton viewdbs =*/new JButton( + MessageManager.getString("action.select_ddbb")); + databaseButt.addActionListener(new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent arg0) + { + hidePanel(); + database.showDialog(); + } + }); databaseButt.setFont(JvSwingUtils.getLabelFont()); database.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - debounceTrap++; String currentSelection = database.getSelectedItem(); + if (currentSelection == null) + { + close_actionPerformed(null); + } + + showPanel(); - if (currentSelection.equalsIgnoreCase("pdb") - && (database.action == KeyEvent.VK_ENTER || ((debounceTrap % 2) == 0))) + if ("pdb".equalsIgnoreCase(currentSelection)) { pdbSourceAction(); } - else if (currentSelection.equalsIgnoreCase("uniprot") - && (database.action == KeyEvent.VK_ENTER || ((debounceTrap % 2) == 0))) + else if ("uniprot".equalsIgnoreCase(currentSelection)) { uniprotSourceAction(); } @@ -406,7 +508,6 @@ public class SequenceFetcher extends JPanel implements Runnable this.add(jPanel3, java.awt.BorderLayout.CENTER); this.add(jPanel2, java.awt.BorderLayout.NORTH); jScrollPane1.getViewport().add(textArea); - } private void pdbSourceAction() @@ -813,10 +914,8 @@ public class SequenceFetcher extends JPanel implements Runnable Cache.log.info( "Error retrieving " + accession + " from " + proxy.getDbName(), e); - } finally - { - return success; } + return success; } /** @@ -836,7 +935,6 @@ public class SequenceFetcher extends JPanel implements Runnable for (String q : queries) { - DBRefEntry[] found = null; DBRefEntry dbr = new DBRefEntry(); dbr.setSource(proxy.getDbSource()); dbr.setVersion(null); @@ -847,8 +945,9 @@ public class SequenceFetcher extends JPanel implements Runnable { if (rs[r] != null) { - found = DBRefUtils.searchRefs(rs[r].getDBRefs(), accId); - if (found != null && found.length > 0) + List found = DBRefUtils.searchRefs(rs[r].getDBRefs(), + accId); + if (!found.isEmpty()) { rfound = true; break; @@ -921,7 +1020,10 @@ public class SequenceFetcher extends JPanel implements Runnable { af.hideFeatureColumns(SequenceOntologyI.EXON, false); } - + if (newAlframes != null) + { + newAlframes.add(af); + } Desktop.addInternalFrame(af, title, AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); @@ -968,4 +1070,22 @@ public class SequenceFetcher extends JPanel implements Runnable { this.progressIndicator = progressIndicator; } + + /** + * Make this panel visible (after a selection has been made in the database + * chooser) + */ + void showPanel() + { + frame.setVisible(true); + } + + /** + * Hide this panel (on clicking the database button to open the database + * chooser) + */ + void hidePanel() + { + frame.setVisible(false); + } } diff --git a/src/jalview/gui/SequenceRenderer.java b/src/jalview/gui/SequenceRenderer.java index f215f9d..3003c01 100755 --- a/src/jalview/gui/SequenceRenderer.java +++ b/src/jalview/gui/SequenceRenderer.java @@ -32,12 +32,6 @@ import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; -/** - * DOCUMENT ME! - * - * @author $author$ - * @version $Revision$ - */ public class SequenceRenderer implements jalview.api.SequenceRenderer { final static int CHAR_TO_UPPER = 'A' - 'a'; @@ -94,6 +88,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer @Override public ColorI getResidueBoxColour(SequenceI seq, int i) { + // rate limiting step when rendering overview for lots of groups allGroups = av.getAlignment().findAllGroups(seq); if (inCurrentSequenceGroup(i)) @@ -420,7 +415,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer } if (!isarep && av.getShowUnconserved()) { - s = getDisplayChar(srep, i, s, '.', currentSequenceGroup); + s = getDisplayChar(srep, i, s, '.', null); } @@ -450,12 +445,17 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer { // TODO - use currentSequenceGroup rather than alignment // currentSequenceGroup.getConsensus() - char conschar = (usesrep) ? (currentGroup == null ? av.getAlignment() + char conschar = (usesrep) ? (currentGroup == null + || position < currentGroup.getStartRes() + || position > currentGroup.getEndRes() ? av.getAlignment() .getSeqrep().getCharAt(position) : (currentGroup.getSeqrep() != null ? currentGroup.getSeqrep() .getCharAt(position) : av.getAlignment().getSeqrep() .getCharAt(position))) - : (currentGroup != null && currentGroup.getConsensus() != null) ? currentGroup + : (currentGroup != null && currentGroup.getConsensus() != null + && position >= currentGroup.getStartRes() + && position <= currentGroup.getEndRes() && currentGroup + .getConsensus().annotations.length > position) ? currentGroup .getConsensus().annotations[position].displayCharacter .charAt(0) : av.getAlignmentConsensusAnnotation().annotations[position].displayCharacter diff --git a/src/jalview/gui/SliderPanel.java b/src/jalview/gui/SliderPanel.java index e1e70dc..c3fec4f 100755 --- a/src/jalview/gui/SliderPanel.java +++ b/src/jalview/gui/SliderPanel.java @@ -220,7 +220,7 @@ public class SliderPanel extends GSliderPanel } PIDSlider.setTitle(MessageManager - .formatMessage("label.percentage_identity_thereshold", + .formatMessage("label.percentage_identity_threshold", new String[] { source })); if (ap.av.getAlignment().getGroups() != null) diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java index 617224f..1bc85d2 100644 --- a/src/jalview/gui/SplitFrame.java +++ b/src/jalview/gui/SplitFrame.java @@ -37,6 +37,8 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.beans.PropertyVetoException; +import java.util.Arrays; +import java.util.List; import java.util.Map.Entry; import javax.swing.AbstractAction; @@ -206,6 +208,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI final AlignmentI bottomAlignment = bottomViewport.getAlignment(); boolean topAnnotations = topViewport.isShowAnnotation(); boolean bottomAnnotations = bottomViewport.isShowAnnotation(); + // TODO need number of visible sequences here, not #sequences - how? int topCount = topAlignment.getHeight(); int bottomCount = bottomAlignment.getHeight(); int topCharHeight = topViewport.getViewStyle().getCharHeight(); @@ -223,6 +226,11 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI + (bottomAnnotations ? bottomViewport.calcPanelHeight() : 0); double ratio = ((double) topHeight) / (topHeight + bottomHeight); + /* + * limit to 0.2 <= ratio <= 0.8 to avoid concealing all sequences + */ + ratio = Math.min(ratio, 0.8d); + ratio = Math.max(ratio, 0.2d); setRelativeDividerLocation(ratio); } @@ -328,6 +336,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI actioned = true; e.consume(); } + break; default: } return actioned; @@ -704,6 +713,17 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI } /** + * return the AlignFrames held by this container + * + * @return { Top alignFrame (Usually CDS), Bottom AlignFrame (Usually + * Protein)} + */ + public List getAlignFrames() + { + return Arrays.asList(new AlignFrame[] { (AlignFrame) getTopFrame(), + (AlignFrame) getBottomFrame() }); + } + /** * Replace Cmd-F Find action with our version. This is necessary because the * 'default' Finder searches in the first AlignFrame it finds. We need it to * search in the half of the SplitFrame that has the mouse. diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java index d924e73..33c7ff3 100644 --- a/src/jalview/gui/StructureChooser.java +++ b/src/jalview/gui/StructureChooser.java @@ -33,8 +33,10 @@ import jalview.fts.core.FTSRestRequest; import jalview.fts.core.FTSRestResponse; import jalview.fts.service.pdb.PDBFTSRestClient; import jalview.jbgui.GStructureChooser; +import jalview.structure.StructureMapping; import jalview.structure.StructureSelectionManager; import jalview.util.MessageManager; +import jalview.ws.DBRefFetcher; import jalview.ws.sifts.SiftsSettings; import java.awt.event.ItemEvent; @@ -44,6 +46,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.Vector; import javax.swing.JCheckBox; @@ -147,8 +150,8 @@ public class StructureChooser extends GStructureChooser implements { long startTime = System.currentTimeMillis(); pdbRestCleint = PDBFTSRestClient.getInstance(); - Collection wantedFields = pdbRestCleint - .getAllDefaulDisplayedDataColumns(); + Collection wantedFields = pdbDocFieldPrefs + .getStructureSummaryFields(); discoveredStructuresSet = new LinkedHashSet(); HashSet errors = new HashSet(); @@ -185,7 +188,8 @@ public class StructureChooser extends GStructureChooser implements if (discoveredStructuresSet != null && !discoveredStructuresSet.isEmpty()) { - tbl_summary.setModel(FTSRestResponse.getTableModel(lastPdbRequest, + getResultTable().setModel( + FTSRestResponse.getTableModel(lastPdbRequest, discoveredStructuresSet)); structuresDiscovered = true; noOfStructuresFound = discoveredStructuresSet.size(); @@ -247,7 +251,7 @@ public class StructureChooser extends GStructureChooser implements boolean isPDBRefsFound = false; boolean isUniProtRefsFound = false; StringBuilder queryBuilder = new StringBuilder(); - HashSet seqRefs = new LinkedHashSet(); + Set seqRefs = new LinkedHashSet(); if (seq.getAllPDBEntries() != null) { @@ -255,9 +259,8 @@ public class StructureChooser extends GStructureChooser implements { if (isValidSeqName(entry.getId())) { - queryBuilder.append("pdb_id") - .append(":") -.append(entry.getId().toLowerCase()) + queryBuilder.append("pdb_id:") + .append(entry.getId().toLowerCase()) .append(" OR "); isPDBRefsFound = true; // seqRefs.add(entry.getId()); @@ -273,21 +276,18 @@ public class StructureChooser extends GStructureChooser implements { if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT)) { - queryBuilder -.append("uniprot_accession").append(":") + queryBuilder.append("uniprot_accession:") .append(getDBRefId(dbRef)) .append(" OR "); - queryBuilder -.append("uniprot_id") - .append(":") - .append(getDBRefId(dbRef)).append(" OR "); + queryBuilder.append("uniprot_id:").append(getDBRefId(dbRef)) + .append(" OR "); isUniProtRefsFound = true; } else if (dbRef.getSource().equalsIgnoreCase(DBRefSource.PDB)) { - queryBuilder.append("pdb_id") - .append(":").append(getDBRefId(dbRef).toLowerCase()) + queryBuilder.append("pdb_id:") + .append(getDBRefId(dbRef).toLowerCase()) .append(" OR "); isPDBRefsFound = true; } @@ -330,17 +330,17 @@ public class StructureChooser extends GStructureChooser implements } /** - * Remove the following special characters from input string +, -, &, |, !, (, - * ), {, }, [, ], ^, ", ~, *, ?, :, \ + * Remove the following special characters from input string +, -, &, !, (, ), + * {, }, [, ], ^, ", ~, *, ?, :, \ * * @param seqName * @return */ - private static String sanitizeSeqName(String seqName) + static String sanitizeSeqName(String seqName) { Objects.requireNonNull(seqName); return seqName.replaceAll("\\[\\d*\\]", "") - .replaceAll("[^\\dA-Za-z|]", "").replaceAll("\\s+", "+"); + .replaceAll("[^\\dA-Za-z|_]", "").replaceAll("\\s+", "+"); } @@ -396,8 +396,8 @@ public class StructureChooser extends GStructureChooser implements long startTime = System.currentTimeMillis(); pdbRestCleint = PDBFTSRestClient.getInstance(); lbl_loading.setVisible(true); - Collection wantedFields = pdbRestCleint - .getAllDefaulDisplayedDataColumns(); + Collection wantedFields = pdbDocFieldPrefs + .getStructureSummaryFields(); Collection filteredResponse = new HashSet(); HashSet errors = new HashSet(); @@ -406,7 +406,6 @@ public class StructureChooser extends GStructureChooser implements FTSRestRequest pdbRequest = new FTSRestRequest(); if (fieldToFilterBy.equalsIgnoreCase("uniprot_coverage")) { - System.out.println(">>>>>> Filtering with uniprot coverate"); pdbRequest.setAllowEmptySeq(false); pdbRequest.setResponseSize(1); pdbRequest.setFieldToSearchBy("("); @@ -454,15 +453,18 @@ public class StructureChooser extends GStructureChooser implements Collection reorderedStructuresSet = new LinkedHashSet(); reorderedStructuresSet.addAll(filteredResponse); reorderedStructuresSet.addAll(discoveredStructuresSet); - tbl_summary.setModel(FTSRestResponse.getTableModel( + getResultTable().setModel( + FTSRestResponse.getTableModel( lastPdbRequest, reorderedStructuresSet)); - 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); + FTSRestResponse.configureTableColumn(getResultTable(), + wantedFields, tempUserPrefs); + getResultTable().getColumn("Ref Sequence").setPreferredWidth(120); + getResultTable().getColumn("Ref Sequence").setMinWidth(100); + getResultTable().getColumn("Ref Sequence").setMaxWidth(200); // Update table selection model here - tbl_summary.addRowSelectionInterval(0, filterResponseCount - 1); + getResultTable().addRowSelectionInterval(0, + filterResponseCount - 1); mainFrame.setTitle(MessageManager.formatMessage( "label.structure_chooser_filter_time", totalTime)); } @@ -529,8 +531,6 @@ public class StructureChooser extends GStructureChooser implements { cmb_filterOption.addItem(new FilterOption("Best Quality", "overall_quality", VIEWS_FILTER)); - cmb_filterOption.addItem(new FilterOption("Most UniProt Coverage", - "uniprot_coverage", VIEWS_FILTER)); cmb_filterOption.addItem(new FilterOption("Best Resolution", "resolution", VIEWS_FILTER)); cmb_filterOption.addItem(new FilterOption("Most Protein Chain", @@ -591,7 +591,7 @@ public class StructureChooser extends GStructureChooser implements String currentView = selectedFilterOpt.getView(); if (currentView == VIEWS_FILTER) { - if (tbl_summary.getSelectedRows().length > 0) + if (getResultTable().getSelectedRows().length > 0) { btn_view.setEnabled(true); } @@ -729,19 +729,21 @@ public class StructureChooser extends GStructureChooser implements String currentView = selectedFilterOpt.getView(); if (currentView == VIEWS_FILTER) { - int pdbIdColIndex = tbl_summary.getColumn("PDB Id") + int pdbIdColIndex = getResultTable().getColumn("PDB Id") .getModelIndex(); - int refSeqColIndex = tbl_summary.getColumn("Ref Sequence") + int refSeqColIndex = getResultTable().getColumn("Ref Sequence") .getModelIndex(); - int[] selectedRows = tbl_summary.getSelectedRows(); + int[] selectedRows = getResultTable().getSelectedRows(); PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length]; int count = 0; ArrayList selectedSeqsToView = new ArrayList(); for (int row : selectedRows) { - String pdbIdStr = tbl_summary.getValueAt(row, pdbIdColIndex) + String pdbIdStr = getResultTable().getValueAt(row, + pdbIdColIndex) .toString(); - SequenceI selectedSeq = (SequenceI) tbl_summary.getValueAt(row, + SequenceI selectedSeq = (SequenceI) getResultTable() + .getValueAt(row, refSeqColIndex); selectedSeqsToView.add(selectedSeq); PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr); @@ -800,7 +802,15 @@ public class StructureChooser extends GStructureChooser implements if (pdbEntry == null) { pdbEntry = new PDBEntry(); - pdbEntry.setId(pdbIdStr); + if (pdbIdStr.split(":").length > 1) + { + pdbEntry.setId(pdbIdStr.split(":")[0]); + pdbEntry.setChainCode(pdbIdStr.split(":")[1].toUpperCase()); + } + else + { + pdbEntry.setId(pdbIdStr); + } pdbEntry.setType(PDBEntry.Type.PDB); selectedSequence.getDatasetSequence().addPDBId(pdbEntry); } @@ -825,7 +835,7 @@ public class StructureChooser extends GStructureChooser implements launchStructureViewer(ssm, new PDBEntry[] { fileEntry }, ap, new SequenceI[] { selectedSequence }); } - mainFrame.dispose(); + closeAction(); } }).start(); } @@ -849,42 +859,77 @@ public class StructureChooser extends GStructureChooser implements final PDBEntry[] pdbEntriesToView, final AlignmentPanel alignPanel, SequenceI[] sequences) { - ssm.setProgressBar("Launching PDB structure viewer.."); + ssm.setProgressBar(MessageManager + .getString("status.launching_3d_structure_viewer")); final StructureViewer sViewer = new StructureViewer(ssm); if (SiftsSettings.isMapWithSifts()) { + ArrayList seqsWithoutSourceDBRef = new ArrayList(); + int p = 0; + // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a + // real PDB ID. For moment, we can also safely do this if there is already + // a known mapping between the PDBEntry and the sequence. for (SequenceI seq : sequences) { - if (seq.getSourceDBRef() == null) - { - ssm.setProgressBar(null); - ssm.setProgressBar("Fetching Database refs.."); - new jalview.ws.DBRefFetcher(sequences, null, null, null, false) - .fetchDBRefs(true); - break; - } - } - } - if (pdbEntriesToView.length > 1) + PDBEntry pdbe = pdbEntriesToView[p++]; + if (pdbe != null && pdbe.getFile() != null) { - ArrayList seqsMap = new ArrayList(); - for (SequenceI seq : sequences) + StructureMapping[] smm = ssm.getMapping(pdbe.getFile()); + if (smm != null && smm.length > 0) { - seqsMap.add(new SequenceI[] { seq }); + for (StructureMapping sm : smm) + { + if (sm.getSequence() == seq) + { + continue; + } + } } - 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 + if (seq.getPrimaryDBRefs().size() == 0) { - ssm.setProgressBar(null); - ssm.setProgressBar("Fetching PDB Structure for " - + pdbEntriesToView[0].getId()); - sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel); + seqsWithoutSourceDBRef.add(seq); + continue; + } + } + if (!seqsWithoutSourceDBRef.isEmpty()) + { + int y = seqsWithoutSourceDBRef.size(); + ssm.setProgressBar(null); + ssm.setProgressBar(MessageManager.formatMessage( + "status.fetching_dbrefs_for_sequences_without_valid_refs", + y)); + SequenceI[] seqWithoutSrcDBRef = new SequenceI[y]; + int x = 0; + for (SequenceI fSeq : seqsWithoutSourceDBRef) + { + seqWithoutSrcDBRef[x++] = fSeq; } + new DBRefFetcher(seqWithoutSrcDBRef).fetchDBRefs(true); + } + } + if (pdbEntriesToView.length > 1) + { + ArrayList seqsMap = new ArrayList(); + for (SequenceI seq : sequences) + { + seqsMap.add(new SequenceI[] { seq }); + } + SequenceI[][] collatedSeqs = seqsMap.toArray(new SequenceI[0][0]); + ssm.setProgressBar(null); + ssm.setProgressBar(MessageManager + .getString("status.fetching_3d_structures_for_selected_entries")); + sViewer.viewStructures(pdbEntriesToView, collatedSeqs, alignPanel); + } + else + { + ssm.setProgressBar(null); + ssm.setProgressBar(MessageManager.formatMessage( + "status.fetching_3d_structures_for", + pdbEntriesToView[0].getId())); + sViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel); + } } /** @@ -943,6 +988,9 @@ public class StructureChooser extends GStructureChooser implements isValidPBDEntry = false; if (txt_search.getText().length() > 0) { + String searchTerm = txt_search.getText().toLowerCase(); + searchTerm = searchTerm.split(":")[0]; + // System.out.println(">>>>> search term : " + searchTerm); List wantedFields = new ArrayList(); FTSRestRequest pdbRequest = new FTSRestRequest(); pdbRequest.setAllowEmptySeq(false); @@ -950,7 +998,7 @@ public class StructureChooser extends GStructureChooser implements pdbRequest.setFieldToSearchBy("(pdb_id:"); pdbRequest.setWantedFields(wantedFields); pdbRequest - .setSearchTerm(txt_search.getText().toLowerCase() + ")"); +.setSearchTerm(searchTerm + ")"); pdbRequest.setAssociatedSequence(selectedSequence); pdbRestCleint = PDBFTSRestClient.getInstance(); wantedFields.add(pdbRestCleint.getPrimaryKeyColumn()); diff --git a/src/jalview/gui/TextColourChooser.java b/src/jalview/gui/TextColourChooser.java index 2ec1c18..37cf6b6 100644 --- a/src/jalview/gui/TextColourChooser.java +++ b/src/jalview/gui/TextColourChooser.java @@ -73,7 +73,7 @@ public class TextColourChooser final JPanel col2 = new JPanel(); col2.setPreferredSize(new Dimension(40, 20)); col2.setBorder(BorderFactory.createEtchedBorder()); - col2.setToolTipText(MessageManager.getString("label.ligth_colour")); + col2.setToolTipText(MessageManager.getString("label.light_colour")); col2.setBackground(new Color(original2)); final JPanel bigpanel = new JPanel(new BorderLayout()); JPanel panel = new JPanel(); @@ -82,7 +82,7 @@ public class TextColourChooser new JLabel( "" + MessageManager - .getString("label.select_dark_light_set_thereshold") + .getString("label.select_dark_light_set_threshold") + ""), BorderLayout.NORTH); panel.add(col1); panel.add(slider); @@ -134,7 +134,7 @@ public class TextColourChooser ap, bigpanel, MessageManager - .getString("label.adjunst_foreground_text_colour_thereshold"), + .getString("label.adjunst_foreground_text_colour_threshold"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null); diff --git a/src/jalview/gui/TreeCanvas.java b/src/jalview/gui/TreeCanvas.java index 149a587..874513e 100755 --- a/src/jalview/gui/TreeCanvas.java +++ b/src/jalview/gui/TreeCanvas.java @@ -61,6 +61,7 @@ import java.util.Vector; import javax.swing.JColorChooser; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; /** @@ -176,13 +177,13 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, tree.findHeight(tree.getTopNode()); // Now have to calculate longest name based on the leaves - Vector leaves = tree.findLeaves(tree.getTopNode(), new Vector()); + Vector leaves = tree.findLeaves(tree.getTopNode()); boolean has_placeholders = false; longestName = ""; for (int i = 0; i < leaves.size(); i++) { - SequenceNode lf = (SequenceNode) leaves.elementAt(i); + SequenceNode lf = leaves.elementAt(i); if (lf.isPlaceholder()) { @@ -750,21 +751,27 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, } /** - * DOCUMENT ME! + * Empty method to satisfy the MouseListener interface * * @param e - * DOCUMENT ME! */ @Override public void mouseReleased(MouseEvent e) { + /* + * isPopupTrigger is set on mouseReleased on Windows + */ + if (e.isPopupTrigger()) + { + chooseSubtreeColour(); + e.consume(); // prevent mouseClicked happening + } } /** - * DOCUMENT ME! + * Empty method to satisfy the MouseListener interface * * @param e - * DOCUMENT ME! */ @Override public void mouseEntered(MouseEvent e) @@ -772,10 +779,9 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, } /** - * DOCUMENT ME! + * Empty method to satisfy the MouseListener interface * * @param e - * DOCUMENT ME! */ @Override public void mouseExited(MouseEvent e) @@ -783,47 +789,57 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, } /** - * DOCUMENT ME! + * Handles a mouse click on a tree node (clicks elsewhere are handled in + * mousePressed). Click selects the sub-tree, double-click swaps leaf nodes + * order, right-click opens a dialogue to choose colour for the sub-tree. * * @param e - * DOCUMENT ME! */ @Override public void mouseClicked(MouseEvent evt) { - if (highlightNode != null) + if (highlightNode == null) { - if (evt.isPopupTrigger()) - { - Color col = JColorChooser.showDialog(this, - MessageManager.getString("label.select_subtree_colour"), - highlightNode.color); - if (col != null) - { - setColor(highlightNode, col); - } - } - else if (evt.getClickCount() > 1) + return; + } + + if (evt.getClickCount() > 1) + { + tree.swapNodes(highlightNode); + tree.reCount(tree.getTopNode()); + tree.findHeight(tree.getTopNode()); + } + else + { + Vector leaves = tree.findLeaves(highlightNode); + + for (int i = 0; i < leaves.size(); i++) { - tree.swapNodes(highlightNode); - tree.reCount(tree.getTopNode()); - tree.findHeight(tree.getTopNode()); + SequenceI seq = (SequenceI) leaves.elementAt(i) + .element(); + treeSelectionChanged(seq); } - else - { - Vector leaves = new Vector(); - tree.findLeaves(highlightNode, leaves); + av.sendSelection(); + } - for (int i = 0; i < leaves.size(); i++) - { - SequenceI seq = (SequenceI) ((SequenceNode) leaves.elementAt(i)) - .element(); - treeSelectionChanged(seq); - } - av.sendSelection(); - } + PaintRefresher.Refresh(tp, av.getSequenceSetId()); + repaint(); + } - PaintRefresher.Refresh(tp, av.getSequenceSetId()); + /** + * Offer the user the option to choose a colour for the highlighted node and + * its children; this colour is also applied to the corresponding sequence ids + * in the alignment + */ + void chooseSubtreeColour() + { + Color col = JColorChooser.showDialog(this, + MessageManager.getString("label.select_subtree_colour"), + highlightNode.color); + if (col != null) + { + setColor(highlightNode, col); + PaintRefresher.Refresh(tp, ap.av.getSequenceSetId()); repaint(); } } @@ -860,16 +876,42 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, } /** - * DOCUMENT ME! + * Handles a mouse press on a sequence name or the tree background canvas + * (click on a node is handled in mouseClicked). The action is to create + * groups by partitioning the tree at the mouse position. Colours for the + * groups (and sequence names) are generated randomly. * * @param e - * DOCUMENT ME! */ @Override public void mousePressed(MouseEvent e) { av.setCurrentTree(tree); + /* + * isPopupTrigger is set for mousePressed (Mac) + * or mouseReleased (Windows) + */ + if (e.isPopupTrigger()) + { + if (highlightNode != null) + { + chooseSubtreeColour(); + } + return; + } + + /* + * defer right-click handling on Windows to + * mouseClicked; note isRightMouseButton + * also matches Cmd-click on Mac which should do + * nothing here + */ + if (SwingUtilities.isRightMouseButton(e)) + { + return; + } + int x = e.getX(); int y = e.getY(); @@ -928,16 +970,16 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, { Color col = new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255)); - setColor((SequenceNode) tree.getGroups().elementAt(i), col.brighter()); + setColor(tree.getGroups().elementAt(i), col.brighter()); - Vector l = tree.findLeaves( - (SequenceNode) tree.getGroups().elementAt(i), new Vector()); + Vector l = tree.findLeaves(tree + .getGroups().elementAt(i)); - Vector sequences = new Vector(); + Vector sequences = new Vector(); for (int j = 0; j < l.size(); j++) { - SequenceI s1 = (SequenceI) ((SequenceNode) l.elementAt(j)) + SequenceI s1 = (SequenceI) l.elementAt(j) .element(); if (!sequences.contains(s1)) @@ -996,18 +1038,15 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, .getCodingComplement(); if (codingComplement != null) { - if (codingComplement != null) + SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, av, + codingComplement); + if (mappedGroup.getSequences().size() > 0) { - SequenceGroup mappedGroup = MappingUtils.mapSequenceGroup(sg, - av, codingComplement); - if (mappedGroup.getSequences().size() > 0) + codingComplement.getAlignment().addGroup(mappedGroup); + for (SequenceI seq : mappedGroup.getSequences()) { - codingComplement.getAlignment().addGroup(mappedGroup); - for (SequenceI seq : mappedGroup.getSequences()) - { - codingComplement.setSequenceColour(seq, - new Colour(col.brighter())); - } + codingComplement.setSequenceColour(seq, + new Colour(col.brighter())); } } } @@ -1026,11 +1065,8 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, { ((AlignViewport) codingComplement).getAlignPanel() .updateAnnotation(); - } - } - } /** diff --git a/src/jalview/gui/TreePanel.java b/src/jalview/gui/TreePanel.java index 9522144..fafa610 100755 --- a/src/jalview/gui/TreePanel.java +++ b/src/jalview/gui/TreePanel.java @@ -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()) @@ -511,8 +520,8 @@ public class TreePanel extends GTreePanel { // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]); - Alignment al = new Alignment((SequenceI[]) alAndColsel[0]); - Alignment dataset = (av != null && av.getAlignment() != null) ? av + AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]); + AlignmentI dataset = (av != null && av.getAlignment() != null) ? av .getAlignment().getDataset() : null; if (dataset != null) { @@ -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 diff --git a/src/jalview/gui/VamsasApplication.java b/src/jalview/gui/VamsasApplication.java index 7588e07..afb6df4 100644 --- a/src/jalview/gui/VamsasApplication.java +++ b/src/jalview/gui/VamsasApplication.java @@ -321,6 +321,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource Thread udthread = new Thread(new Runnable() { + @Override public void run() { Cache.log.info("Jalview updating to the Vamsas Session."); @@ -639,6 +640,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource final VamsasApplication client = this; vclient.addDocumentUpdateHandler(new PropertyChangeListener() { + @Override public void propertyChange(PropertyChangeEvent evt) { Cache.log.debug("Dealing with document update event."); @@ -656,6 +658,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource uk.ac.vamsas.client.Events.DOCUMENT_REQUESTTOCLOSE, new PropertyChangeListener() { + @Override public void propertyChange(PropertyChangeEvent evt) { if (client.promptUser) @@ -774,6 +777,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource { String last = null; + @Override public void handleMessage(Message message) { if (vobj2jv == null) @@ -998,6 +1002,7 @@ public class VamsasApplication implements SelectionSource, VamsasSource selecter = new SelectionListener() { + @Override public void selection(SequenceGroup seqsel, ColumnSelection colsel, SelectionSource source) { @@ -1065,11 +1070,10 @@ public class VamsasApplication implements SelectionSource, VamsasSource { // gather selected columns outwith the sequence positions // too - for (Object obj : colsel.getSelected()) + for (Integer ival : colsel.getSelected()) { - int ival = ((Integer) obj).intValue(); Pos p = new Pos(); - p.setI(ival + 1); + p.setI(ival.intValue() + 1); range.addPos(p); } } diff --git a/src/jalview/io/AlignFile.java b/src/jalview/io/AlignFile.java index 984eff6..5760fbe 100755 --- a/src/jalview/io/AlignFile.java +++ b/src/jalview/io/AlignFile.java @@ -89,14 +89,14 @@ public abstract class AlignFile extends FileParse /** * Constructor which parses the data from a file of some specified type. * - * @param inFile - * Filename to read from. + * @param dataObject + * Filename, URL or Pasted String to read from. * @param type - * What type of file to read from (File, URL) + * What type of file to read from (File, URL, Pasted String) */ - public AlignFile(String inFile, String type) throws IOException + public AlignFile(String dataObject, String type) throws IOException { - this(true, inFile, type); + this(true, dataObject, type); } /** @@ -105,16 +105,16 @@ public abstract class AlignFile extends FileParse * * @param parseImmediately * if false, need to call 'doParse()' to begin parsing data - * @param inFile - * Filename to read from. + * @param dataObject + * Filename, URL or Pasted String to read from. * @param type * What type of file to read from (File, URL) * @throws IOException */ - public AlignFile(boolean parseImmediately, String inFile, String type) + public AlignFile(boolean parseImmediately, String dataObject, String type) throws IOException { - super(inFile, type); + super(dataObject, type); initData(); if (parseImmediately) { diff --git a/src/jalview/io/AnnotationFile.java b/src/jalview/io/AnnotationFile.java index 9ef9ee8..ad3b630 100755 --- a/src/jalview/io/AnnotationFile.java +++ b/src/jalview/io/AnnotationFile.java @@ -1763,6 +1763,10 @@ public class AnnotationFile */ public String printCSVAnnotations(AlignmentAnnotation[] annotations) { + if (annotations == null) + { + return ""; + } StringBuffer sp = new StringBuffer(); for (int i = 0; i < annotations.length; i++) { diff --git a/src/jalview/io/AppletFormatAdapter.java b/src/jalview/io/AppletFormatAdapter.java index 1a639f1..552f00e 100755 --- a/src/jalview/io/AppletFormatAdapter.java +++ b/src/jalview/io/AppletFormatAdapter.java @@ -26,7 +26,9 @@ import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; -import jalview.structure.StructureViewSettings; +import jalview.datamodel.PDBEntry.Type; +import jalview.ext.jmol.JmolParser; +import jalview.structure.StructureImportSettings; import jalview.util.MessageManager; import java.io.File; @@ -276,33 +278,12 @@ public class AppletFormatAdapter alignFile = new JPredFile(inFile, type); ((JPredFile) alignFile).removeNonSequences(); } - else if (format.equals("PDB")) + else if (format.equals("PDB") || format.equalsIgnoreCase("mmCIF")) { - // TODO obtain config value from preference settings. - // Set value to 'true' to test PDB processing with Jmol: JAL-1213 - boolean isParseWithJMOL = false; - if (isParseWithJMOL) - { - StructureViewSettings.addSettings(annotFromStructure, - localSecondaryStruct, serviceSecondaryStruct); - alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure, - localSecondaryStruct, serviceSecondaryStruct, inFile, - type); - } - else - { - StructureViewSettings.setShowSeqFeatures(true); - alignFile = new MCview.PDBfile(annotFromStructure, - localSecondaryStruct, serviceSecondaryStruct, inFile, - type); - } - } - else if (format.equals("mmCIF")) - { - StructureViewSettings.addSettings(annotFromStructure, + StructureImportSettings.addSettings(annotFromStructure, localSecondaryStruct, serviceSecondaryStruct); - alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure, - localSecondaryStruct, serviceSecondaryStruct, inFile, type); + alignFile = new JmolParser(inFile, type); + ((StructureFile) alignFile).setDbRefType(format); } else if (format.equals("STH")) { @@ -433,24 +414,24 @@ public class AppletFormatAdapter boolean isParseWithJMOL = false; if (isParseWithJMOL) { - StructureViewSettings.addSettings(annotFromStructure, + StructureImportSettings.addSettings(annotFromStructure, localSecondaryStruct, serviceSecondaryStruct); - alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure, - localSecondaryStruct, serviceSecondaryStruct, source); + alignFile = new JmolParser(source); } else { - StructureViewSettings.setShowSeqFeatures(true); + StructureImportSettings.setShowSeqFeatures(true); alignFile = new MCview.PDBfile(annotFromStructure, localSecondaryStruct, serviceSecondaryStruct, source); } + ((StructureFile) alignFile).setDbRefType(Type.PDB); } - else if (format.equals("mmCIF")) + else if (format.equalsIgnoreCase("mmCIF")) { - StructureViewSettings.addSettings(annotFromStructure, + StructureImportSettings.addSettings(annotFromStructure, localSecondaryStruct, serviceSecondaryStruct); - alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure, - localSecondaryStruct, serviceSecondaryStruct, source); + alignFile = new JmolParser(source); + ((StructureFile) alignFile).setDbRefType(Type.MMCIF); } else if (format.equals("STH")) { diff --git a/src/jalview/io/FeaturesFile.java b/src/jalview/io/FeaturesFile.java index 40f83d3..46467c5 100755 --- a/src/jalview/io/FeaturesFile.java +++ b/src/jalview/io/FeaturesFile.java @@ -36,7 +36,6 @@ import jalview.io.gff.GffHelperFactory; import jalview.io.gff.GffHelperI; import jalview.schemes.FeatureColour; import jalview.schemes.UserColourScheme; -import jalview.util.Format; import jalview.util.MapList; import jalview.util.ParseHtmlBodyAndLinks; import jalview.util.StringUtils; @@ -233,12 +232,18 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI else if (ft.equalsIgnoreCase("endgroup")) { // We should check whether this is the current group, - // but at present theres no way of showing more than 1 group + // but at present there's no way of showing more than 1 group featureGroup = null; } else { - parseFeatureColour(line, ft, gffColumns, colours); + String colscheme = gffColumns[1]; + FeatureColourI colour = FeatureColour + .parseJalviewFeatureColour(colscheme); + if (colour != null) + { + colours.put(ft, colour); + } } continue; } @@ -744,50 +749,14 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI // viewed features // TODO: decide if feature links should also be written here ? Iterator en = featureColours.keySet().iterator(); - String featureType, color; while (en.hasNext()) { - featureType = en.next(); - FeatureColourI fc = featureColours.get(featureType); - if (fc.isSimpleColour()) - { - color = Format.getHexString(fc.getColour()); - } - else - { - color = (fc.isColourByLabel() ? "label|" : "") - + Format.getHexString(fc.getMinColour()) + "|" - + Format.getHexString(fc.getMaxColour()) - + (fc.isAutoScaled() ? "|" : "|abso|") + fc.getMin() + "|" - + fc.getMax() + "|"; - if (fc.isBelowThreshold()) - { - color += "below"; - } - else if (fc.isAboveThreshold()) - { - color += "above"; - } - // add the value - color += "|" + fc.getThreshold(); - } -// else -// { -// color += "none"; -// } - // else - // { - // // legacy support for integer objects containing colour triplet - // values - // color = Format.getHexString(new Color(Integer - // .parseInt(fc.toString()))); - // } - out.append(featureType); - out.append(TAB); - out.append(color); - out.append(newline); + String featureType = en.next(); + FeatureColourI colour = featureColours.get(featureType); + out.append(colour.toJalviewFormat(featureType)).append(newline); } } + // Work out which groups are both present and visible List groups = new ArrayList(); int groupIndex = 0; @@ -839,12 +808,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 && !featureColours - .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 @@ -852,47 +821,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("") == -1) + if (sequenceFeature.links != null + && sequenceFeature.getDescription().indexOf("") == -1) { out.append(""); } - 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("" + label + ""); + out.append(" " + label + + ""); } } - if (features[j].getDescription().indexOf("") == -1) + if (sequenceFeature.getDescription().indexOf("") == -1) { out.append(""); } @@ -902,15 +872,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); } @@ -1022,7 +992,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) diff --git a/src/jalview/io/FormatAdapter.java b/src/jalview/io/FormatAdapter.java index 4ec077f..6d94616 100755 --- a/src/jalview/io/FormatAdapter.java +++ b/src/jalview/io/FormatAdapter.java @@ -305,20 +305,6 @@ public class FormatAdapter extends AppletFormatAdapter return this.formatSequences(format, alignment, suffix); } - public AlignmentI readFile(String inFile, String type, String format) - throws java.io.IOException - { - AlignmentI al = super.readFile(inFile, type, format); - return al; - } - - public AlignmentI readFromFile(FileParse source, String format) - throws java.io.IOException - { - AlignmentI al = super.readFromFile(source, format); - return al; - } - /** * validate format is valid for IO in Application. This is basically the * AppletFormatAdapter.isValidFormat call with additional checks for diff --git a/src/jalview/io/HtmlSvgOutput.java b/src/jalview/io/HtmlSvgOutput.java index 79c055e..147d7f5 100644 --- a/src/jalview/io/HtmlSvgOutput.java +++ b/src/jalview/io/HtmlSvgOutput.java @@ -52,6 +52,13 @@ public class HtmlSvgOutput AlignmentPanel ap; + private IProgressIndicator pIndicator; + + private long pSessionId; + + private boolean headless; + + public HtmlSvgOutput(File file, AlignmentPanel ap) { this.av = ap.av; @@ -62,20 +69,16 @@ public class HtmlSvgOutput public void generateHtmlSvgOutput(File file) { - IProgressIndicator pIndicator = ap.alignFrame; - long pSessionId = System.currentTimeMillis(); + pIndicator = ap.alignFrame; + pSessionId = System.currentTimeMillis(); try { - boolean headless = (System.getProperty("java.awt.headless") != null && System + headless = (System.getProperty("java.awt.headless") != null && System .getProperty("java.awt.headless").equals("true")); if (file == null) { - if (pIndicator != null && !headless) - { - pIndicator.setProgressBar(MessageManager.formatMessage( - "status.waiting_for_user_to_select_output_file", "HTML"), - pSessionId); - } + setProgressMessage(MessageManager.formatMessage( + "status.waiting_for_user_to_select_output_file", "HTML")); JalviewFileChooser chooser = getHTMLChooser(); chooser.setFileView(new jalview.io.JalviewFileView()); chooser.setDialogTitle(ap.alignFrame.getTitle()); @@ -87,135 +90,164 @@ public class HtmlSvgOutput jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser .getSelectedFile().getParent()); file = chooser.getSelectedFile(); + ap.alignFrame.repaint(); } else { - - if (pIndicator != null && !headless) - { - pIndicator.setProgressBar(MessageManager.formatMessage( - "status.cancelled_image_export_operation", "HTML"), - pSessionId); - } + setProgressMessage(MessageManager.formatMessage( + "status.cancelled_image_export_operation", "HTML")); return; } } - - AlignmentDimension aDimension = ap.getAlignmentDimension(); - SVGGraphics2D g1 = new SVGGraphics2D(aDimension.getWidth(), - aDimension.getHeight()); - SVGGraphics2D g2 = new SVGGraphics2D(aDimension.getWidth(), - aDimension.getHeight()); - - String renderStyle = jalview.bin.Cache.getDefault("HTML_RENDERING", - "Prompt each time"); - - // If we need to prompt, and if the GUI is visible then - // Prompt for rendering style - if (renderStyle.equalsIgnoreCase("Prompt each time") - && !(System.getProperty("java.awt.headless") != null && System - .getProperty("java.awt.headless").equals("true"))) - { - HTMLOptions svgOption = new HTMLOptions(); - renderStyle = svgOption.getValue(); - - if (renderStyle == null || svgOption.cancelled) - { - return; - } - } - - if (renderStyle.equalsIgnoreCase("lineart")) - { - g1.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE, - SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR); - g2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE, - SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR); - } - printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0, g1, - g2); - - String titleSvgData = g1.getSVGDocument(); - String alignSvgData = g2.getSVGDocument(); - String jsonData = null; - boolean isEmbbedBioJSON = Boolean.valueOf(jalview.bin.Cache - .getDefault("EXPORT_EMBBED_BIOJSON", "true")); - if (isEmbbedBioJSON) + } catch (Exception e) + { + pIndicator.setProgressBar(MessageManager.formatMessage( + "info.error_creating_file", "HTML"), pSessionId); + e.printStackTrace(); + return; + } + final File fileX = file; + new Thread() + { + @Override + public void run() { - AlignExportSettingI exportSettings = new AlignExportSettingI() + try { - @Override - public boolean isExportHiddenSequences() + setProgressMessage(null); + setProgressMessage(MessageManager +.formatMessage( + "status.exporting_alignment_as_x_file", "HTML")); + AlignmentDimension aDimension = ap.getAlignmentDimension(); + SVGGraphics2D g1 = new SVGGraphics2D(aDimension.getWidth(), + aDimension.getHeight()); + SVGGraphics2D g2 = new SVGGraphics2D(aDimension.getWidth(), + aDimension.getHeight()); + + String renderStyle = jalview.bin.Cache.getDefault( + "HTML_RENDERING", "Prompt each time"); + + // If we need to prompt, and if the GUI is visible then + // Prompt for rendering style + if (renderStyle.equalsIgnoreCase("Prompt each time") + && !(System.getProperty("java.awt.headless") != null && System + .getProperty("java.awt.headless").equals("true"))) { - return true; + HTMLOptions svgOption = new HTMLOptions(); + renderStyle = svgOption.getValue(); + + if (renderStyle == null || svgOption.cancelled) + { + setProgressMessage(MessageManager.formatMessage( + "status.cancelled_image_export_operation", "HTML")); + return; + } } - @Override - public boolean isExportHiddenColumns() + if (renderStyle.equalsIgnoreCase("Lineart")) { - return true; + g1.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE, + SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR); + g2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE, + SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR); } - - @Override - public boolean isExportAnnotations() + printUnwrapped(aDimension.getWidth(), aDimension.getHeight(), 0, + g1, g2); + + String titleSvgData = g1.getSVGDocument(); + String alignSvgData = g2.getSVGDocument(); + String jsonData = null; + boolean isEmbbedBioJSON = Boolean.valueOf(jalview.bin.Cache + .getDefault("EXPORT_EMBBED_BIOJSON", "true")); + if (isEmbbedBioJSON) { - return true; + AlignExportSettingI exportSettings = new AlignExportSettingI() + { + @Override + public boolean isExportHiddenSequences() + { + return true; + } + + @Override + public boolean isExportHiddenColumns() + { + return true; + } + + @Override + public boolean isExportAnnotations() + { + return true; + } + + @Override + public boolean isExportFeatures() + { + return true; + } + + @Override + public boolean isExportGroups() + { + return true; + } + + @Override + public boolean isCancelled() + { + return false; + } + + }; + AlignmentExportData exportData = jalview.gui.AlignFrame + .getAlignmentForExport(JSONFile.FILE_DESC, av, + exportSettings); + jsonData = new FormatAdapter(ap, exportData.getSettings()) + .formatSequences(JSONFile.FILE_DESC, + exportData.getAlignment(), + exportData.getOmitHidden(), + exportData.getStartEndPostions(), + av.getColumnSelection()); } - - @Override - public boolean isExportFeatures() - { - return true; - } - - @Override - public boolean isExportGroups() + String htmlData = getHtml(titleSvgData, alignSvgData, jsonData); + FileOutputStream out = new FileOutputStream(fileX); + out.write(htmlData.getBytes()); + out.flush(); + out.close(); + if (!(System.getProperty("java.awt.headless") != null && System + .getProperty("java.awt.headless").equals("true"))) { - return true; + jalview.util.BrowserLauncher.openURL("file:///" + fileX); } + } catch (OutOfMemoryError err) + { + System.out.println("########################\n" + + "OUT OF MEMORY " + fileX + "\n" + + "########################"); + new OOMWarning("Creating Image for " + fileX, err); + } catch (Exception e) + { + e.printStackTrace(); + pIndicator.setProgressBar(MessageManager.formatMessage( + "info.error_creating_file", "HTML"), pSessionId); + } + setProgressMessage(MessageManager.formatMessage( + "status.export_complete", "HTML")); + } + }.start(); - @Override - public boolean isCancelled() - { - return false; - } + } - }; - AlignmentExportData exportData = jalview.gui.AlignFrame - .getAlignmentForExport(JSONFile.FILE_DESC, av, - exportSettings); - jsonData = new FormatAdapter(ap, exportData.getSettings()) - .formatSequences(JSONFile.FILE_DESC, - exportData.getAlignment(), - exportData.getOmitHidden(), - exportData.getStartEndPostions(), - av.getColumnSelection()); - } - String htmlData = getHtml(titleSvgData, alignSvgData, jsonData); - FileOutputStream out = new FileOutputStream(file); - out.write(htmlData.getBytes()); - out.flush(); - out.close(); - if (!(System.getProperty("java.awt.headless") != null && System - .getProperty("java.awt.headless").equals("true"))) - { - jalview.util.BrowserLauncher.openURL("file:///" + file); - } - if (pIndicator != null && !headless) - { - pIndicator.setProgressBar(MessageManager.formatMessage( - "status.export_complete", "HTML"), pSessionId); - } - } catch (OutOfMemoryError err) + private void setProgressMessage(String message) + { + if (pIndicator != null && !headless) { - System.out.println("########################\n" + "OUT OF MEMORY " - + file + "\n" + "########################"); - new OOMWarning("Creating Image for " + file, err); - } catch (Exception e) + pIndicator.setProgressBar(message, pSessionId); + } + else { - e.printStackTrace(); - pIndicator.setProgressBar(MessageManager.formatMessage( - "info.error_creating_file", "HTML"), pSessionId); + System.out.println(message); } } diff --git a/src/jalview/io/IdentifyFile.java b/src/jalview/io/IdentifyFile.java index 71f4237..889359f 100755 --- a/src/jalview/io/IdentifyFile.java +++ b/src/jalview/io/IdentifyFile.java @@ -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(" headers = new ArrayList(); + Hashtable seqhash = new Hashtable(); try { + String line; while ((line = nextLine()) != null) { StringTokenizer str = new StringTokenizer(line); + String key = null; while (str.hasMoreTokens()) { String inStr = str.nextToken(); @@ -93,31 +94,31 @@ public class MSFfile extends AlignFile if (inStr.indexOf("Name:") != -1) { key = str.nextToken(); - headers.addElement(key); + headers.add(key); } - // if line has // set SeqFlag to 1 so we know sequences are coming + // if line has // set SeqFlag so we know sequences are coming if (inStr.indexOf("//") != -1) { seqFlag = true; } // Process lines as sequence lines if seqFlag is set - if ((inStr.indexOf("//") == -1) && (seqFlag == true)) + if ((inStr.indexOf("//") == -1) && seqFlag) { - // seqeunce id is the first field + // sequence id is the first field key = inStr; - StringBuffer tempseq; + StringBuilder tempseq; // Get sequence from hash if it exists if (seqhash.containsKey(key)) { - tempseq = (StringBuffer) seqhash.get(key); + tempseq = seqhash.get(key); } else { - tempseq = new StringBuffer(); + tempseq = new StringBuilder(64); seqhash.put(key, tempseq); } @@ -125,7 +126,8 @@ public class MSFfile extends AlignFile while (str.hasMoreTokens()) { // append the word to the sequence - tempseq.append(str.nextToken()); + String sequenceBlock = str.nextToken(); + tempseq.append(sequenceBlock); } } } @@ -139,11 +141,11 @@ public class MSFfile extends AlignFile this.noSeqs = headers.size(); // Add sequences to the hash - for (i = 0; i < headers.size(); i++) + for (int i = 0; i < headers.size(); i++) { - if (seqhash.get(headers.elementAt(i)) != null) + if (seqhash.get(headers.get(i)) != null) { - String head = headers.elementAt(i).toString(); + String head = headers.get(i); String seq = seqhash.get(head).toString(); if (maxLength < head.length()) @@ -151,8 +153,11 @@ public class MSFfile extends AlignFile maxLength = head.length(); } - // Replace ~ with a sensible gap character - seq = seq.replace('~', '-'); + /* + * replace ~ (leading/trailing positions) with the gap character; + * use '.' as this is the internal gap character required by MSF + */ + seq = seq.replace('~', '.'); Sequence newSeq = parseId(head); @@ -163,7 +168,7 @@ public class MSFfile extends AlignFile else { System.err.println("MSFFile Parser: Can't find sequence for " - + headers.elementAt(i)); + + headers.get(i)); } } } @@ -211,15 +216,16 @@ public class MSFfile extends AlignFile * * @return DOCUMENT ME! */ - public String print(SequenceI[] seqs) + public String print(SequenceI[] sqs) { - boolean is_NA = jalview.util.Comparison.isNucleotide(seqs); + boolean is_NA = Comparison.isNucleotide(sqs); - SequenceI[] s = new SequenceI[seqs.length]; + SequenceI[] s = new SequenceI[sqs.length]; - StringBuffer out = new StringBuffer("!!" + (is_NA ? "NA" : "AA") - + "_MULTIPLE_ALIGNMENT 1.0"); + StringBuilder out = new StringBuilder(256); + out.append("!!").append(is_NA ? "NA" : "AA") + .append("_MULTIPLE_ALIGNMENT 1.0"); // TODO: JBPNote : Jalview doesn't remember NA or AA yet. out.append(newline); out.append(newline); @@ -227,14 +233,16 @@ public class MSFfile extends AlignFile int maxid = 0; int i = 0; - while ((i < seqs.length) && (seqs[i] != null)) + while ((i < sqs.length) && (sqs[i] != null)) { - // Replace all internal gaps with . and external spaces with ~ - s[i] = new Sequence(seqs[i].getName(), seqs[i].getSequenceAsString() - .replace('-', '.'), seqs[i].getStart(), seqs[i].getEnd()); + /* + * modify to MSF format: uses '.' for internal gaps, + * and '~' for leading or trailing gaps + */ + String seqString = sqs[i].getSequenceAsString() + .replace('-', '.'); - StringBuffer sb = new StringBuffer(); - sb.append(s[i].getSequence()); + StringBuilder sb = new StringBuilder(seqString); for (int ii = 0; ii < sb.length(); ii++) { @@ -259,12 +267,12 @@ public class MSFfile extends AlignFile break; } } + s[i] = new Sequence(sqs[i].getName(), sb.toString(), + sqs[i].getStart(), sqs[i].getEnd()); - s[i].setSequence(sb.toString()); - - if (s[i].getSequence().length > max) + if (sb.length() > max) { - max = s[i].getSequence().length; + max = sb.length(); } i++; diff --git a/src/jalview/io/PDBFeatureSettings.java b/src/jalview/io/PDBFeatureSettings.java index 0c2f030..19dc9ee 100644 --- a/src/jalview/io/PDBFeatureSettings.java +++ b/src/jalview/io/PDBFeatureSettings.java @@ -3,7 +3,7 @@ package jalview.io; import jalview.api.ColorI; import jalview.api.FeatureColourI; import jalview.schemes.Colour; -import jalview.schemes.FeatureColourAdapter; +import jalview.schemes.FeatureColour; import jalview.schemes.FeatureSettingsAdapter; public class PDBFeatureSettings extends FeatureSettingsAdapter @@ -25,7 +25,7 @@ public class PDBFeatureSettings extends FeatureSettingsAdapter { if (type.equalsIgnoreCase(FEATURE_INSERTION)) { - return new FeatureColourAdapter() + return new FeatureColour() { @Override diff --git a/src/jalview/io/PfamFile.java b/src/jalview/io/PfamFile.java index 71cc7f0..667da9f 100755 --- a/src/jalview/io/PfamFile.java +++ b/src/jalview/io/PfamFile.java @@ -108,7 +108,7 @@ public class PfamFile extends AlignFile } if (spces + 1 < line.length()) { - tempseq.append(line.substring(spces + 1)); + tempseq.append(line.substring(spces + 1).trim()); } } diff --git a/src/jalview/io/RnamlFile.java b/src/jalview/io/RnamlFile.java index 0941a6f..f48f825 100644 --- a/src/jalview/io/RnamlFile.java +++ b/src/jalview/io/RnamlFile.java @@ -20,7 +20,7 @@ */ package jalview.io; -import jalview.analysis.SecStrConsensus.SimpleBP; +import jalview.analysis.Rna; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; import jalview.datamodel.Sequence; @@ -32,6 +32,7 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import com.stevesoft.pat.Regex; @@ -79,6 +80,7 @@ public class RnamlFile extends AlignFile * * @see jalview.io.AlignFile#parse() */ + @Override public void parse() throws IOException { if (System.getProperty("java.version").indexOf("1.6") > -1 @@ -134,10 +136,10 @@ public class RnamlFile extends AlignFile result = RNAFactory.loadSecStrRNAML(getReader()); - ArrayList allarray = new ArrayList(); - ArrayList> BP = new ArrayList(); - ArrayList strucinarray = new ArrayList(); - SequenceI[] seqs = new SequenceI[result.size()]; + // ArrayList allarray = new ArrayList(); + // ArrayList> BP = new ArrayList(); + // ArrayList strucinarray = new ArrayList(); + SequenceI[] sqs = new SequenceI[result.size()]; for (int i = 0; i < result.size(); i++) { @@ -157,9 +159,9 @@ public class RnamlFile extends AlignFile id += "." + i; } } - seqs[i] = new Sequence(id, seq, begin, end); + sqs[i] = new Sequence(id, seq, begin, end); - seqs[i].setEnd(seqs[i].findPosition(seqs[i].getLength())); + sqs[i].setEnd(sqs[i].findPosition(sqs[i].getLength())); String[] annot = new String[rna.length()]; Annotation[] ann = new Annotation[rna.length()]; @@ -170,9 +172,8 @@ public class RnamlFile extends AlignFile } for (int k = 0; k < rna.length(); k++) { - ann[k] = new Annotation(annot[k], "", - jalview.schemes.ResidueProperties.getRNASecStrucState( - annot[k]).charAt(0), 0f); + ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState( + annot[k]).charAt(0), 0f); } AlignmentAnnotation align = new AlignmentAnnotation( @@ -181,17 +182,17 @@ public class RnamlFile extends AlignFile + current.getID() : "", ann); - seqs[i].addAlignmentAnnotation(align); - seqs[i].setRNA(result.get(i)); + sqs[i].addAlignmentAnnotation(align); + sqs[i].setRNA(result.get(i)); - allarray.add(strucinarray); + // allarray.add(strucinarray); annotations.addElement(align); - BP.add(align.bps); + // BP.add(align.bps); } - setSeqs(seqs); + setSeqs(sqs); } public static String print(SequenceI[] s) @@ -199,13 +200,14 @@ public class RnamlFile extends AlignFile return "not yet implemented"; } + @Override public String print() { System.out.print("print :"); return print(getSeqsAsArray()); } - public ArrayList getRNA() + public List getRNA() { return result; } diff --git a/src/jalview/io/StockholmFile.java b/src/jalview/io/StockholmFile.java index 23c4d21..bec7d82 100644 --- a/src/jalview/io/StockholmFile.java +++ b/src/jalview/io/StockholmFile.java @@ -23,6 +23,7 @@ */ package jalview.io; +import jalview.analysis.Rna; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; @@ -31,6 +32,7 @@ import jalview.datamodel.Mapping; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.schemes.ResidueProperties; import jalview.util.Format; import jalview.util.MessageManager; @@ -72,8 +74,12 @@ import fr.orsay.lri.varna.models.rna.RNA; */ public class StockholmFile extends AlignFile { - // static Logger logger = Logger.getLogger("jalview.io.StockholmFile"); - protected ArrayList result; + private static final Regex OPEN_PAREN = new Regex("(<|\\[)", "("); + + private static final Regex CLOSE_PAREN = new Regex("(>|\\])", ")"); + + private static final Regex DETECT_BRACKETS = new Regex( + "(<|>|\\[|\\]|\\(|\\))"); StringBuffer out; // output buffer @@ -101,6 +107,7 @@ public class StockholmFile extends AlignFile super(source); } + @Override public void initData() { super.initData(); @@ -118,7 +125,7 @@ public class StockholmFile extends AlignFile fr = new FileReader(inFile); BufferedReader r = new BufferedReader(fr); - result = null; + List result = null; try { result = RNAFactory.loadSecStrStockholm(r); @@ -155,9 +162,8 @@ public class StockholmFile extends AlignFile for (int k = 0; k < rna.length(); k++) { - ann[k] = new Annotation(annot[k], "", - jalview.schemes.ResidueProperties.getRNASecStrucState( - annot[k]).charAt(0), 0f); + ann[k] = new Annotation(annot[k], "", Rna.getRNASecStrucState( + annot[k]).charAt(0), 0f); } AlignmentAnnotation align = new AlignmentAnnotation("Sec. str.", @@ -178,6 +184,7 @@ public class StockholmFile extends AlignFile * @throws IOException * If there is an error with the input file */ + @Override public void parse() throws IOException { StringBuffer treeString = new StringBuffer(); @@ -533,8 +540,7 @@ public class StockholmFile extends AlignFile } else { - // throw new IOException(MessageManager.formatMessage( - // "exception.error_parsing_line", new String[] { line })); + // throw new IOException("Error parsing " + line); System.err.println(">> missing annotation: " + line); } } @@ -788,19 +794,13 @@ public class StockholmFile extends AlignFile } protected static AlignmentAnnotation parseAnnotationRow( - Vector annotation, String label, String annots) + Vector annotation, String label, + String annots) { String convert1, convert2 = null; - // Convert all bracket types to parentheses - Regex openparen = new Regex("(<|\\[)", "("); - Regex closeparen = new Regex("(>|\\])", ")"); - - // Detect if file is RNA by looking for bracket types - Regex detectbrackets = new Regex("(<|>|\\[|\\]|\\(|\\))"); - - convert1 = openparen.replaceAll(annots); - convert2 = closeparen.replaceAll(convert1); + convert1 = OPEN_PAREN.replaceAll(annots); + convert2 = CLOSE_PAREN.replaceAll(convert1); annots = convert2; String type = label; @@ -827,15 +827,15 @@ public class StockholmFile extends AlignFile { // if (" .-_".indexOf(pos) == -1) { - if (detectbrackets.search(pos)) + if (DETECT_BRACKETS.search(pos)) { - ann.secondaryStructure = jalview.schemes.ResidueProperties - .getRNASecStrucState(pos).charAt(0); + ann.secondaryStructure = Rna.getRNASecStrucState( + pos).charAt(0); } else { - ann.secondaryStructure = jalview.schemes.ResidueProperties - .getDssp3state(pos).charAt(0); + ann.secondaryStructure = ResidueProperties.getDssp3state(pos) + .charAt(0); } if (ann.secondaryStructure == pos.charAt(0)) @@ -853,10 +853,10 @@ public class StockholmFile extends AlignFile els[i] = ann; } AlignmentAnnotation annot = null; - Enumeration e = annotation.elements(); + Enumeration e = annotation.elements(); while (e.hasMoreElements()) { - annot = (AlignmentAnnotation) e.nextElement(); + annot = e.nextElement(); if (annot.label.equals(type)) { break; @@ -1106,6 +1106,7 @@ public class StockholmFile extends AlignFile return seq; } + @Override public String print() { out = new StringBuffer(); @@ -1119,6 +1120,7 @@ public class StockholmFile extends AlignFile } private static Hashtable typeIds = null; + static { if (typeIds == null) diff --git a/src/jalview/io/StructureFile.java b/src/jalview/io/StructureFile.java index 3a7419c..0bc6a73 100644 --- a/src/jalview/io/StructureFile.java +++ b/src/jalview/io/StructureFile.java @@ -8,8 +8,9 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; import jalview.datamodel.PDBEntry; +import jalview.datamodel.PDBEntry.Type; import jalview.datamodel.SequenceI; -import jalview.structure.StructureViewSettings; +import jalview.structure.StructureImportSettings; import java.awt.Color; import java.io.IOException; @@ -25,7 +26,8 @@ public abstract class StructureFile extends AlignFile private String id; - private String dbRefType; + private PDBEntry.Type dbRefType; + /** * set to true to add derived sequence annotations (temp factor read from * file, or computed secondary structure) to the alignment @@ -66,19 +68,19 @@ public abstract class StructureFile extends AlignFile public void xferSettings() { - this.visibleChainAnnotation = StructureViewSettings + this.visibleChainAnnotation = StructureImportSettings .isVisibleChainAnnotation(); - this.predictSecondaryStructure = StructureViewSettings - .isPredictSecondaryStructure(); - this.externalSecondaryStructure = StructureViewSettings + this.predictSecondaryStructure = StructureImportSettings + .isProcessSecondaryStructure(); + this.externalSecondaryStructure = StructureImportSettings .isExternalSecondaryStructure(); } - public StructureFile(boolean parseImmediately, String inFile, String type) + public StructureFile(boolean parseImmediately, String dataObject, String type) throws IOException { - super(parseImmediately, inFile, type); + super(parseImmediately, dataObject, type); } public StructureFile(boolean a, FileParse fp) throws IOException @@ -97,7 +99,7 @@ public abstract class StructureFile extends AlignFile pdbSequence.setName(getId() + "|" + pdbSequence.getName()); PDBEntry entry = new PDBEntry(); entry.setId(getId()); - entry.setType(this.dbRefType); + entry.setType(getStructureFileType()); entry.setProperty(new Hashtable()); if (chain.id != null) { @@ -115,16 +117,13 @@ public abstract class StructureFile extends AlignFile DBRefEntry sourceDBRef = new DBRefEntry(); sourceDBRef.setAccessionId(getId()); 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); - + // TODO: specify version for 'PDB' database ref if it is read from a file. + // TODO: decide if jalview.io should be creating primary refs! + sourceDBRef.setVersion(""); + pdbSequence.addPDBId(entry); + pdbSequence.addDBRef(sourceDBRef); + SequenceI chainseq = pdbSequence; seqs.addElement(chainseq); - AlignmentAnnotation[] chainannot = chainseq.getAnnotation(); if (chainannot != null && visibleChainAnnotation) @@ -138,6 +137,26 @@ public abstract class StructureFile extends AlignFile return chainseq; } + /** + * filetype of structure file - default is PDB + */ + String structureFileType = PDBEntry.Type.PDB.toString(); + + protected void setStructureFileType(String structureFileType) + { + this.structureFileType = structureFileType; + } + + /** + * filetype of last file processed + * + * @return + */ + public String getStructureFileType() + { + return structureFileType; + } + @SuppressWarnings({ "unchecked", "rawtypes" }) protected void processPdbFileWithAnnotate3d(List rna) throws Exception @@ -185,8 +204,7 @@ public abstract class StructureFile extends AlignFile @SuppressWarnings("unchecked") protected void replaceAndUpdateChains(List prot, - AlignmentI al, - String pep, boolean b) + AlignmentI al, String pep, boolean b) { List> replaced = AlignSeq .replaceMatchingSeqsWith(seqs, annotations, prot, al, pep, @@ -259,8 +277,7 @@ public abstract class StructureFile extends AlignFile } @SuppressWarnings({ "unchecked", "rawtypes" }) - private void processWithJmolParser(List prot) - throws Exception + private void processWithJmolParser(List prot) throws Exception { try { @@ -269,18 +286,15 @@ public abstract class StructureFile extends AlignFile if (cl != null) { 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(), + .getConstructor(new Class[] { FileParse.class }); + final Object[] args = new Object[] { new FileParse(getDataName(), type) }; - StructureViewSettings.setShowSeqFeatures(false); - StructureViewSettings.setVisibleChainAnnotation(false); - StructureViewSettings - .setPredictSecondaryStructure(predictSecondaryStructure); - StructureViewSettings + StructureImportSettings.setShowSeqFeatures(false); + StructureImportSettings.setVisibleChainAnnotation(false); + StructureImportSettings + .setProcessSecondaryStructure(predictSecondaryStructure); + StructureImportSettings .setExternalSecondaryStructure(externalSecondaryStructure); Object jmf = constructor.newInstance(args); AlignmentI al = new Alignment((SequenceI[]) cl.getMethod( @@ -303,13 +317,14 @@ public abstract class StructureFile extends AlignFile } catch (ClassNotFoundException q) { } + StructureImportSettings.setShowSeqFeatures(true); } public PDBChain findChain(String id) throws Exception { for (PDBChain chain : getChains()) { - if (chain.id.equalsIgnoreCase(id)) + if (chain.id.equals(id)) { return chain; } @@ -406,13 +421,18 @@ public abstract class StructureFile extends AlignFile this.chains = chains; } - public String getDbRefType() + public Type getDbRefType() { return dbRefType; } public void setDbRefType(String dbRefType) { + this.dbRefType = Type.getType(dbRefType); + } + + public void setDbRefType(Type dbRefType) + { this.dbRefType = dbRefType; } diff --git a/src/jalview/io/vamsas/Tree.java b/src/jalview/io/vamsas/Tree.java index 0bf4096..a3781a7 100644 --- a/src/jalview/io/vamsas/Tree.java +++ b/src/jalview/io/vamsas/Tree.java @@ -291,8 +291,10 @@ public class Tree extends DatastoreItem } } if (alsq.size() < sequences.length) + { Cache.log .warn("Not recovered all alignment sequences for given set of input sequence CIGARS"); + } return alsq; } @@ -306,15 +308,18 @@ public class Tree extends DatastoreItem public void UpdateSequenceTreeMap(TreePanel tp) { if (tp == null || tree == null) + { return; - Vector leaves = new Vector(); + } + if (tp.getTree() == null) { Cache.log.warn("Not updating SequenceTreeMap for " + tree.getVorbaId()); return; } - tp.getTree().findLeaves(tp.getTree().getTopNode(), leaves); + Vector leaves = tp.getTree().findLeaves( + tp.getTree().getTopNode()); Treenode[] tn = tree.getTreenode(); // todo: select nodes for this // particular tree int sz = tn.length; @@ -371,8 +376,7 @@ public class Tree extends DatastoreItem */ public Treenode[] makeTreeNodes(NJTree ntree, Newick newick) { - Vector leaves = new Vector(); - ntree.findLeaves(ntree.getTopNode(), leaves); + Vector leaves = ntree.findLeaves(ntree.getTopNode()); Vector tnv = new Vector(); Enumeration l = leaves.elements(); Hashtable nodespecs = new Hashtable(); @@ -473,7 +477,9 @@ public class Tree extends DatastoreItem --occurence; } else + { bn = null; + } } return bn; } diff --git a/src/jalview/jbgui/GAlignFrame.java b/src/jalview/jbgui/GAlignFrame.java index b2eb094..70333f4 100755 --- a/src/jalview/jbgui/GAlignFrame.java +++ b/src/jalview/jbgui/GAlignFrame.java @@ -136,8 +136,6 @@ public class GAlignFrame extends JInternalFrame public JCheckBoxMenuItem showSeqFeatures = new JCheckBoxMenuItem(); - public JCheckBoxMenuItem showSeqFeaturesHeight = new JCheckBoxMenuItem(); - JMenuItem copy = new JMenuItem(); JMenuItem cut = new JMenuItem(); @@ -294,34 +292,51 @@ public class GAlignFrame extends JInternalFrame @Override public void mousePressed(MouseEvent evt) { - if (evt.isPopupTrigger()) + if (evt.isPopupTrigger()) // Mac { - radioItem.removeActionListener(radioItem.getActionListeners()[0]); - - int option = JOptionPane.showInternalConfirmDialog( - jalview.gui.Desktop.desktop, - MessageManager - .getString("label.remove_from_default_list"), - MessageManager - .getString("label.remove_user_defined_colour"), - JOptionPane.YES_NO_OPTION); - if (option == JOptionPane.YES_OPTION) - { - jalview.gui.UserDefinedColours - .removeColourFromDefaults(radioItem.getText()); - colourMenu.remove(radioItem); - } - else + offerRemoval(radioItem); + } + } + + @Override + public void mouseReleased(MouseEvent evt) + { + if (evt.isPopupTrigger()) // Windows + { + offerRemoval(radioItem); + } + } + + /** + * @param radioItem + */ + void offerRemoval(final JRadioButtonMenuItem radioItem) + { + radioItem.removeActionListener(radioItem.getActionListeners()[0]); + + int option = JOptionPane.showInternalConfirmDialog( + jalview.gui.Desktop.desktop, + MessageManager + .getString("label.remove_from_default_list"), + MessageManager + .getString("label.remove_user_defined_colour"), + JOptionPane.YES_NO_OPTION); + if (option == JOptionPane.YES_OPTION) + { + jalview.gui.UserDefinedColours + .removeColourFromDefaults(radioItem.getText()); + colourMenu.remove(radioItem); + } + else + { + radioItem.addActionListener(new ActionListener() { - radioItem.addActionListener(new ActionListener() + @Override + public void actionPerformed(ActionEvent evt) { - @Override - public void actionPerformed(ActionEvent evt) - { - userDefinedColour_actionPerformed(evt); - } - }); - } + userDefinedColour_actionPerformed(evt); + } + }); } } }); @@ -1591,7 +1606,7 @@ public class GAlignFrame extends JInternalFrame }); JMenuItem modifyPID = new JMenuItem( - MessageManager.getString("label.modify_identity_thereshold")); + MessageManager.getString("label.modify_identity_threshold")); modifyPID.addActionListener(new ActionListener() { @Override @@ -1601,7 +1616,7 @@ public class GAlignFrame extends JInternalFrame } }); modifyConservation.setText(MessageManager - .getString("label.modify_conservation_thereshold")); + .getString("label.modify_conservation_threshold")); modifyConservation.addActionListener(new ActionListener() { @Override @@ -2032,7 +2047,19 @@ public class GAlignFrame extends JInternalFrame @Override public void mousePressed(MouseEvent e) { - tabbedPane_mousePressed(e); + if (e.isPopupTrigger()) // Mac + { + tabbedPane_mousePressed(e); + } + } + + @Override + public void mouseReleased(MouseEvent e) + { + if (e.isPopupTrigger()) // Windows + { + tabbedPane_mousePressed(e); + } } }); tabbedPane.addFocusListener(new FocusAdapter() @@ -2520,13 +2547,6 @@ public class GAlignFrame extends JInternalFrame } - protected void showSeqFeaturesHeight_actionPerformed( - ActionEvent actionEvent) - { - // TODO Auto-generated method stub - - } - protected void justifyRightMenuItem_actionPerformed(ActionEvent e) { // TODO Auto-generated method stub diff --git a/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java b/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java index a9e3112..157dddd 100644 --- a/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java +++ b/src/jalview/jbgui/GCutAndPasteHtmlTransfer.java @@ -106,6 +106,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame ok.setText(MessageManager.getString("label.new_window")); ok.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { ok_actionPerformed(e); @@ -114,6 +115,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame cancel.setText(MessageManager.getString("action.close")); cancel.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { cancel_actionPerformed(e); @@ -123,6 +125,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame close.setText(MessageManager.getString("action.close")); close.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { cancel_actionPerformed(e); @@ -137,6 +140,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame .getMenuShortcutKeyMask(), false)); selectAll.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { selectAll_actionPerformed(e); @@ -149,6 +153,7 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame .getMenuShortcutKeyMask(), false)); save.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { save_actionPerformed(e); @@ -163,15 +168,23 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame textarea.setFont(new java.awt.Font("Monospaced", Font.PLAIN, 12)); textarea.addMouseListener(new java.awt.event.MouseAdapter() { + @Override public void mousePressed(MouseEvent e) { textarea_mousePressed(e); } + + @Override + public void mouseReleased(MouseEvent e) + { + textarea_mousePressed(e); + } }); editMenu.setText(MessageManager.getString("action.edit")); copyItem.setText(MessageManager.getString("action.copy")); copyItem.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { copyItem_actionPerformed(e); diff --git a/src/jalview/jbgui/GCutAndPasteTransfer.java b/src/jalview/jbgui/GCutAndPasteTransfer.java index acf1581..21705f0 100755 --- a/src/jalview/jbgui/GCutAndPasteTransfer.java +++ b/src/jalview/jbgui/GCutAndPasteTransfer.java @@ -103,6 +103,7 @@ public class GCutAndPasteTransfer extends JInternalFrame ok.setText(MessageManager.getString("label.new_window")); ok.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { ok_actionPerformed(e); @@ -111,6 +112,7 @@ public class GCutAndPasteTransfer extends JInternalFrame cancel.setText(MessageManager.getString("action.close")); cancel.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { cancel_actionPerformed(e); @@ -124,6 +126,7 @@ public class GCutAndPasteTransfer extends JInternalFrame .getMenuShortcutKeyMask(), false)); selectAll.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { selectAll_actionPerformed(e); @@ -136,6 +139,7 @@ public class GCutAndPasteTransfer extends JInternalFrame .getMenuShortcutKeyMask(), false)); save.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { save_actionPerformed(e); @@ -152,15 +156,23 @@ public class GCutAndPasteTransfer extends JInternalFrame textarea.setFont(new java.awt.Font("Monospaced", Font.PLAIN, 12)); textarea.addMouseListener(new java.awt.event.MouseAdapter() { + @Override public void mousePressed(MouseEvent e) { - textarea_mousePressed(e); + textarea_mousePressed(e); // on Mac + } + + @Override + public void mouseReleased(MouseEvent e) + { + textarea_mousePressed(e); // on Windows } }); editMenu.setText(MessageManager.getString("action.edit")); pasteMenu.setText(MessageManager.getString("action.paste")); pasteMenu.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { pasteMenu_actionPerformed(e); @@ -169,6 +181,7 @@ public class GCutAndPasteTransfer extends JInternalFrame copyItem.setText(MessageManager.getString("action.copy")); copyItem.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { copyItem_actionPerformed(e); diff --git a/src/jalview/jbgui/GPreferences.java b/src/jalview/jbgui/GPreferences.java index c4c737c..90053f5 100755 --- a/src/jalview/jbgui/GPreferences.java +++ b/src/jalview/jbgui/GPreferences.java @@ -201,7 +201,7 @@ public class GPreferences extends JPanel /* * Output tab components */ - protected JComboBox epsRendering = new JComboBox(); + protected JComboBox epsRendering = new JComboBox(); protected JLabel userIdWidthlabel = new JLabel(); @@ -1236,7 +1236,7 @@ public class GPreferences extends JPanel .getString("label.open_overview")); openoverv.setHorizontalAlignment(SwingConstants.RIGHT); openoverv.setHorizontalTextPosition(SwingConstants.LEFT); - openoverv.setText(MessageManager.getString(("label.open_overview"))); + openoverv.setText(MessageManager.getString("label.open_overview")); JPanel jPanel2 = new JPanel(); jPanel2.setBounds(new Rectangle(7, 17, 158, 310)); jPanel2.setLayout(new GridLayout(14, 1)); diff --git a/src/jalview/jbgui/GStructureChooser.java b/src/jalview/jbgui/GStructureChooser.java index a1aa0d3..1348e59 100644 --- a/src/jalview/jbgui/GStructureChooser.java +++ b/src/jalview/jbgui/GStructureChooser.java @@ -44,6 +44,8 @@ import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -61,6 +63,7 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import javax.swing.event.InternalFrameEvent; import javax.swing.table.TableColumn; @SuppressWarnings("serial") @@ -165,13 +168,15 @@ public abstract class GStructureChooser extends JPanel implements private JTabbedPane pnl_filter = new JTabbedPane(); - private FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences( + protected FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences( PreferenceSource.STRUCTURE_CHOOSER, PDBFTSRestClient.getInstance()); protected FTSDataColumnI[] previousWantedFields; - protected JTable tbl_summary = new JTable() + protected static Map tempUserPrefs = new HashMap(); + + private JTable tbl_summary = new JTable() { private boolean inLayout; @@ -214,6 +219,8 @@ public abstract class GStructureChooser extends JPanel implements && !inLayout) { resizingColumn.setPreferredWidth(resizingColumn.getWidth()); + String colHeader = resizingColumn.getHeaderValue().toString(); + tempUserPrefs.put(colHeader, resizingColumn.getWidth()); } resizeAndRepaint(); } @@ -269,6 +276,10 @@ public abstract class GStructureChooser extends JPanel implements */ private void jbInit() throws Exception { + Integer width = tempUserPrefs.get("structureChooser.width") == null ? 800 + : tempUserPrefs.get("structureChooser.width"); + Integer height = tempUserPrefs.get("structureChooser.height") == null ? 400 + : tempUserPrefs.get("structureChooser.height"); tbl_summary.setAutoCreateRowSorter(true); tbl_summary.getTableHeader().setReorderingAllowed(false); tbl_summary.addMouseListener(new MouseAdapter() @@ -368,6 +379,7 @@ public abstract class GStructureChooser extends JPanel implements } } evt.consume(); + break; default: return; } @@ -402,7 +414,7 @@ public abstract class GStructureChooser extends JPanel implements @Override public void actionPerformed(ActionEvent e) { - mainFrame.dispose(); + closeAction(); } }); btn_cancel.addKeyListener(new KeyAdapter() @@ -412,7 +424,7 @@ public abstract class GStructureChooser extends JPanel implements { if (evt.getKeyCode() == KeyEvent.VK_ENTER) { - mainFrame.dispose(); + closeAction(); } } }); @@ -440,11 +452,9 @@ public abstract class GStructureChooser extends JPanel implements } }); - scrl_foundStructures.setPreferredSize(new Dimension(500, 300)); - scrl_foundStructures - .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + scrl_foundStructures.setPreferredSize(new Dimension(width, height)); - scrl_localPDB.setPreferredSize(new Dimension(500, 300)); + scrl_localPDB.setPreferredSize(new Dimension(width, height)); scrl_localPDB .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); @@ -452,9 +462,8 @@ public abstract class GStructureChooser extends JPanel implements chk_invertFilter.setFont(new java.awt.Font("Verdana", 0, 12)); chk_rememberSettings.setFont(new java.awt.Font("Verdana", 0, 12)); chk_rememberSettings.setVisible(false); - - txt_search.setToolTipText(MessageManager - .getString("label.enter_pdb_id")); + txt_search.setToolTipText(JvSwingUtils.wrapTooltip(true, + MessageManager.getString("label.enter_pdb_id"))); cmb_filterOption.setToolTipText(MessageManager .getString("info.select_filter_option")); txt_search.getDocument().addDocumentListener(new DocumentListener() @@ -521,9 +530,9 @@ public abstract class GStructureChooser extends JPanel implements btn_cancel.setEnabled(false); btn_view.setVisible(false); btn_cancel.setVisible(false); - previousWantedFields = PDBFTSRestClient.getInstance() - .getAllDefaulDisplayedDataColumns() - .toArray(new FTSDataColumnI[0]); + previousWantedFields = pdbDocFieldPrefs + .getStructureSummaryFields().toArray( + new FTSDataColumnI[0]); } if (sourceTabbedPane.getTitleAt(index) .equals(foundStructureSummary)) @@ -541,7 +550,7 @@ public abstract class GStructureChooser extends JPanel implements } }; pnl_filter.addChangeListener(changeListener); - pnl_filter.setPreferredSize(new Dimension(500, 300)); + pnl_filter.setPreferredSize(new Dimension(width, height)); pnl_filter.add(foundStructureSummary, scrl_foundStructures); pnl_filter.add(configureCols, pdbDocFieldPrefs); @@ -562,24 +571,52 @@ public abstract class GStructureChooser extends JPanel implements statusPanel.add(statusBar, null); this.add(pnl_actionsAndStatus, java.awt.BorderLayout.SOUTH); + mainFrame + .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter() + { + @Override + public void internalFrameClosing(InternalFrameEvent e) + { + closeAction(); + } + }); mainFrame.setVisible(true); mainFrame.setContentPane(this); mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - Desktop.addInternalFrame(mainFrame, frameTitle, 800, 400); + Integer x = tempUserPrefs.get("structureChooser.x"); + Integer y = tempUserPrefs.get("structureChooser.y"); + if (x != null && y != null) + { + mainFrame.setLocation(x, y); + } + Desktop.addInternalFrame(mainFrame, frameTitle, width, height); } + protected void closeAction() + { + // System.out.println(">>>>>>>>>> closing internal frame!!!"); + // System.out.println("width : " + mainFrame.getWidth()); + // System.out.println("heigh : " + mainFrame.getHeight()); + // System.out.println("x : " + mainFrame.getX()); + // System.out.println("y : " + mainFrame.getY()); + tempUserPrefs.put("structureChooser.width", pnl_filter.getWidth()); + tempUserPrefs.put("structureChooser.height", pnl_filter.getHeight()); + tempUserPrefs.put("structureChooser.x", mainFrame.getX()); + tempUserPrefs.put("structureChooser.y", mainFrame.getY()); + mainFrame.dispose(); + } public boolean wantedFieldsUpdated() { if (previousWantedFields == null) { return true; } - - return Arrays.equals( - PDBFTSRestClient.getInstance() - .getAllDefaulDisplayedDataColumns() - .toArray(new FTSDataColumnI[0]), - previousWantedFields) ? false : true; + + FTSDataColumnI[] currentWantedFields = pdbDocFieldPrefs + .getStructureSummaryFields() + .toArray(new FTSDataColumnI[0]); + return Arrays.equals(currentWantedFields, previousWantedFields) ? false + : true; } @@ -753,6 +790,10 @@ public abstract class GStructureChooser extends JPanel implements } } + public JTable getResultTable() + { + return tbl_summary; + } public JComboBox getCmbFilterOption() { return cmb_filterOption; diff --git a/src/jalview/renderer/AnnotationRenderer.java b/src/jalview/renderer/AnnotationRenderer.java index 75099c2..82f6ffb 100644 --- a/src/jalview/renderer/AnnotationRenderer.java +++ b/src/jalview/renderer/AnnotationRenderer.java @@ -22,6 +22,7 @@ package jalview.renderer; import jalview.analysis.AAFrequency; import jalview.analysis.CodingUtils; +import jalview.analysis.Rna; import jalview.analysis.StructureFrequency; import jalview.api.AlignViewportI; import jalview.datamodel.AlignmentAnnotation; @@ -43,8 +44,6 @@ import java.awt.image.ImageObserver; import java.util.BitSet; import java.util.Hashtable; -import com.stevesoft.pat.Regex; - public class AnnotationRenderer { private static final int UPPER_TO_LOWER = 'a' - 'A'; // 32 @@ -75,7 +74,7 @@ public class AnnotationRenderer this.debugRedraw = debugRedraw; } - public void drawStemAnnot(Graphics g, Annotation[] row_annotations, + void drawStemAnnot(Graphics g, Annotation[] row_annotations, int lastSSX, int x, int y, int iconOffset, int startRes, int column, boolean validRes, boolean validEnd) { @@ -83,7 +82,6 @@ public class AnnotationRenderer int sCol = (lastSSX / charWidth) + startRes; int x1 = lastSSX; int x2 = (x * charWidth); - Regex closeparen = new Regex("(\\))"); char dc = (column == 0 || row_annotations[column - 1] == null) ? ' ' : row_annotations[column - 1].secondaryStructure; @@ -93,15 +91,17 @@ public class AnnotationRenderer boolean diffdownstream = !validRes || !validEnd || row_annotations[column] == null || dc != row_annotations[column].secondaryStructure; - // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream); - // If a closing base pair half of the stem, display a backward arrow - if (column > 0 && ResidueProperties.isCloseParenRNA(dc)) - { + if (column > 0 && Rna.isClosingParenthesis(dc)) + { if (diffupstream) // if (validRes && column>1 && row_annotations[column-2]!=null && // dc.equals(row_annotations[column-2].displayCharacter)) { + /* + * if new annotation with a closing base pair half of the stem, + * display a backward arrow + */ g.fillPolygon(new int[] { lastSSX + 5, lastSSX + 5, lastSSX }, new int[] { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3); @@ -114,10 +114,13 @@ public class AnnotationRenderer } else { - // display a forward arrow if (diffdownstream) { + /* + * if annotation ending with an opeing base pair half of the stem, + * display a forward arrow + */ g.fillPolygon(new int[] { x2 - 5, x2 - 5, x2 }, new int[] { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3); x2 -= 5; @@ -195,7 +198,7 @@ public class AnnotationRenderer */ private boolean canClip = false; - public void drawNotCanonicalAnnot(Graphics g, Color nonCanColor, + void drawNotCanonicalAnnot(Graphics g, Color nonCanColor, Annotation[] row_annotations, int lastSSX, int x, int y, int iconOffset, int startRes, int column, boolean validRes, boolean validEnd) @@ -206,7 +209,6 @@ public class AnnotationRenderer int sCol = (lastSSX / charWidth) + startRes; int x1 = lastSSX; int x2 = (x * charWidth); - Regex closeparen = new Regex("}|]|<|[a-z]"); String dc = (column == 0 || row_annotations[column - 1] == null) ? "" : row_annotations[column - 1].displayCharacter; @@ -218,8 +220,7 @@ public class AnnotationRenderer || !dc.equals(row_annotations[column].displayCharacter); // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream); // If a closing base pair half of the stem, display a backward arrow - if (column > 0 && closeparen.search(dc))// closeletter_b.search(dc)||closeletter_c.search(dc)||closeletter_d.search(dc)||closecrochet.search(dc)) - // ) + if (column > 0 && Rna.isClosingParenthesis(dc)) { if (diffupstream) @@ -321,7 +322,7 @@ public class AnnotationRenderer * @param column * @return */ - public int[] getProfileFor(AlignmentAnnotation aa, int column) + int[] getProfileFor(AlignmentAnnotation aa, int column) { // TODO : consider refactoring the global alignment calculation // properties/rendering attributes as a global 'alignment group' which holds @@ -750,7 +751,7 @@ public class AnnotationRenderer validEnd); break; } - + // no break if isRNA - falls through to drawNotCanonicalAnnot! case 'E': if (!isRNA) { @@ -759,6 +760,7 @@ public class AnnotationRenderer validEnd); break; } + // no break if isRNA - fall through to drawNotCanonicalAnnot! case '{': case '}': @@ -866,7 +868,6 @@ public class AnnotationRenderer { validRes = true; } - // x ++; if (row.hasIcons) @@ -881,6 +882,7 @@ public class AnnotationRenderer startRes, column, validRes, validEnd); break; } + // no break if isRNA - fall through to drawNotCanonicalAnnot! case 'E': if (!isRNA) @@ -889,6 +891,7 @@ public class AnnotationRenderer startRes, column, validRes, validEnd); break; } + // no break if isRNA - fall through to drawNotCanonicalAnnot! case '(': case ')': // Stem case for RNA secondary structure @@ -1069,7 +1072,7 @@ public class AnnotationRenderer private Color sdNOTCANONICAL_COLOUR; - public void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX, + void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX, int x, int y, int iconOffset, int startRes, int column, boolean validRes, boolean validEnd) { @@ -1077,7 +1080,7 @@ public class AnnotationRenderer g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2); } - public void drawSheetAnnot(Graphics g, Annotation[] row, + void drawSheetAnnot(Graphics g, Annotation[] row, int lastSSX, int x, int y, int iconOffset, int startRes, int column, boolean validRes, boolean validEnd) @@ -1101,7 +1104,7 @@ public class AnnotationRenderer } - public void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX, + void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX, int x, int y, int iconOffset, int startRes, int column, boolean validRes, boolean validEnd) { @@ -1161,7 +1164,7 @@ public class AnnotationRenderer g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8); } - public void drawLineGraph(Graphics g, AlignmentAnnotation _aa, + void drawLineGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes, int eRes, int y, float min, float max, int graphHeight) { @@ -1254,7 +1257,7 @@ public class AnnotationRenderer } } - public void drawBarGraph(Graphics g, AlignmentAnnotation _aa, + void drawBarGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes, int eRes, float min, float max, int y, boolean renderHistogram, boolean renderProfile, boolean normaliseProfile) @@ -1385,8 +1388,9 @@ public class AnnotationRenderer scl = htn * scale * profl[c++]; lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics() .getFontRenderContext()); - g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance( - wdth, scl / lm.getAscent()))); + Font font = ofont.deriveFont(AffineTransform.getScaleInstance( + wdth, scl / lm.getAscent())); + g.setFont(font); lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g); // Debug - render boxes around characters diff --git a/src/jalview/renderer/ScaleRenderer.java b/src/jalview/renderer/ScaleRenderer.java new file mode 100644 index 0000000..6940f22 --- /dev/null +++ b/src/jalview/renderer/ScaleRenderer.java @@ -0,0 +1,132 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.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 +{ + public final class ScaleMark + { + public final boolean major; + + public final int column; + + public final String text; + + ScaleMark(boolean isMajor, int col, String txt) + { + major = isMajor; + column = col; + text = txt; + } + } + + /** + * 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 of ScaleMark holding boolean: true/false for major/minor mark, + * marker position in alignment column coords, a String to be rendered + * at the position (or null) + */ + public List calculateMarks(AlignViewportI av, + int startx, int endx) + { + 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 marks = new ArrayList(); + 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) + { + 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); + } + } + marks.add(new ScaleMark(true, i - startx - 1, string)); + } + else + { + marks.add(new ScaleMark(false, i - startx - 1, null)); + } + } + return marks; + } + +} diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java index e6e3071..e5f0b57 100644 --- a/src/jalview/renderer/seqfeatures/FeatureRenderer.java +++ b/src/jalview/renderer/seqfeatures/FeatureRenderer.java @@ -20,6 +20,7 @@ */ package jalview.renderer.seqfeatures; +import jalview.api.AlignViewportI; import jalview.api.ColorI; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; @@ -52,6 +53,18 @@ public class FeatureRenderer extends FeatureRendererModel boolean av_validCharWidth, av_isShowSeqFeatureHeight; + private Integer currentColour; + + /** + * Constructor given a viewport + * + * @param viewport + */ + public FeatureRenderer(AlignViewportI viewport) + { + this.av = viewport; + } + protected void updateAvConfig() { av_charHeight = av.getCharHeight(); @@ -269,8 +282,6 @@ public class FeatureRenderer extends FeatureRendererModel int epos; - private Integer currentColour; - public synchronized void drawSequence(Graphics g, final SequenceI seq, int start, int end, int y1) { diff --git a/src/jalview/schemabinding/version2/.castor.cdr b/src/jalview/schemabinding/version2/.castor.cdr index 361fb7c..0a01103 100644 --- a/src/jalview/schemabinding/version2/.castor.cdr +++ b/src/jalview/schemabinding/version2/.castor.cdr @@ -1,4 +1,4 @@ -#Thu Sep 03 10:55:37 BST 2015 +#Mon Jun 20 15:44:52 BST 2016 jalview.schemabinding.version2.ThresholdLine=jalview.schemabinding.version2.descriptors.ThresholdLineDescriptor jalview.schemabinding.version2.SequenceSetProperties=jalview.schemabinding.version2.descriptors.SequenceSetPropertiesDescriptor jalview.schemabinding.version2.StructureState=jalview.schemabinding.version2.descriptors.StructureStateDescriptor diff --git a/src/jalview/schemabinding/version2/JSeq.java b/src/jalview/schemabinding/version2/JSeq.java index 9ca6708..7c6308e 100644 --- a/src/jalview/schemabinding/version2/JSeq.java +++ b/src/jalview/schemabinding/version2/JSeq.java @@ -72,6 +72,16 @@ public class JSeq implements java.io.Serializable private boolean _has_hidden; /** + * Field _viewreference. + */ + private boolean _viewreference; + + /** + * keeps track of state for field: _viewreference + */ + private boolean _has_viewreference; + + /** * Field _featuresList. */ private java.util.Vector _featuresList; @@ -256,6 +266,13 @@ public class JSeq implements java.io.Serializable } /** + */ + public void deleteViewreference() + { + this._has_viewreference = false; + } + + /** * Method enumerateFeatures. * * @return an Enumeration over all jalview.schemabinding.version2.Features @@ -549,6 +566,16 @@ public class JSeq implements java.io.Serializable } /** + * Returns the value of field 'viewreference'. + * + * @return the value of field 'Viewreference'. + */ + public boolean getViewreference() + { + return this._viewreference; + } + + /** * Method hasColour. * * @return true if at least one Colour has been added @@ -589,6 +616,16 @@ public class JSeq implements java.io.Serializable } /** + * Method hasViewreference. + * + * @return true if at least one Viewreference has been added + */ + public boolean hasViewreference() + { + return this._has_viewreference; + } + + /** * Returns the value of field 'hidden'. * * @return the value of field 'Hidden'. @@ -616,6 +653,16 @@ public class JSeq implements java.io.Serializable } /** + * Returns the value of field 'viewreference'. + * + * @return the value of field 'Viewreference'. + */ + public boolean isViewreference() + { + return this._viewreference; + } + + /** * * * @param out @@ -1004,6 +1051,18 @@ public class JSeq implements java.io.Serializable } /** + * Sets the value of field 'viewreference'. + * + * @param viewreference + * the value of field 'viewreference'. + */ + public void setViewreference(final boolean viewreference) + { + this._viewreference = viewreference; + this._has_viewreference = true; + } + + /** * Method unmarshal. * * @param reader diff --git a/src/jalview/schemabinding/version2/descriptors/AnnotationColoursDescriptor.java b/src/jalview/schemabinding/version2/descriptors/AnnotationColoursDescriptor.java index 1d2aad3..5739d90 100644 --- a/src/jalview/schemabinding/version2/descriptors/AnnotationColoursDescriptor.java +++ b/src/jalview/schemabinding/version2/descriptors/AnnotationColoursDescriptor.java @@ -11,6 +11,8 @@ package jalview.schemabinding.version2.descriptors; //- Imported classes and packages -/ //---------------------------------/ +import jalview.schemabinding.version2.AnnotationColours; + /** * Class AnnotationColoursDescriptor. * diff --git a/src/jalview/schemabinding/version2/descriptors/FeaturesDescriptor.java b/src/jalview/schemabinding/version2/descriptors/FeaturesDescriptor.java index a7ffaba..107c06d 100644 --- a/src/jalview/schemabinding/version2/descriptors/FeaturesDescriptor.java +++ b/src/jalview/schemabinding/version2/descriptors/FeaturesDescriptor.java @@ -11,6 +11,8 @@ package jalview.schemabinding.version2.descriptors; //- Imported classes and packages -/ //---------------------------------/ +import jalview.schemabinding.version2.Features; + /** * Class FeaturesDescriptor. * diff --git a/src/jalview/schemabinding/version2/descriptors/JSeqDescriptor.java b/src/jalview/schemabinding/version2/descriptors/JSeqDescriptor.java index 0f000bb..28f23b26 100644 --- a/src/jalview/schemabinding/version2/descriptors/JSeqDescriptor.java +++ b/src/jalview/schemabinding/version2/descriptors/JSeqDescriptor.java @@ -334,6 +334,61 @@ public class JSeqDescriptor extends fieldValidator.setValidator(typeValidator); } desc.setValidator(fieldValidator); + // -- _viewreference + desc = new org.exolab.castor.xml.util.XMLFieldDescriptorImpl( + java.lang.Boolean.TYPE, "_viewreference", "viewreference", + org.exolab.castor.xml.NodeType.Attribute); + handler = new org.exolab.castor.xml.XMLFieldHandler() + { + public java.lang.Object getValue(java.lang.Object object) + throws IllegalStateException + { + JSeq target = (JSeq) object; + if (!target.hasViewreference()) + { + return null; + } + return (target.getViewreference() ? java.lang.Boolean.TRUE + : java.lang.Boolean.FALSE); + } + + public void setValue(java.lang.Object object, java.lang.Object value) + throws IllegalStateException, IllegalArgumentException + { + try + { + JSeq target = (JSeq) object; + // if null, use delete method for optional primitives + if (value == null) + { + target.deleteViewreference(); + return; + } + target.setViewreference(((java.lang.Boolean) value) + .booleanValue()); + } catch (java.lang.Exception ex) + { + throw new IllegalStateException(ex.toString()); + } + } + + public java.lang.Object newInstance(java.lang.Object parent) + { + return null; + } + }; + desc.setHandler(handler); + desc.setMultivalued(false); + addFieldDescriptor(desc); + + // -- validation code for: _viewreference + fieldValidator = new org.exolab.castor.xml.FieldValidator(); + { // -- local scope + org.exolab.castor.xml.validators.BooleanValidator typeValidator; + typeValidator = new org.exolab.castor.xml.validators.BooleanValidator(); + fieldValidator.setValidator(typeValidator); + } + desc.setValidator(fieldValidator); // -- initialize element descriptors // -- _featuresList diff --git a/src/jalview/schemabinding/version2/descriptors/JalviewUserColoursDescriptor.java b/src/jalview/schemabinding/version2/descriptors/JalviewUserColoursDescriptor.java index 77efa7e..d65de13 100644 --- a/src/jalview/schemabinding/version2/descriptors/JalviewUserColoursDescriptor.java +++ b/src/jalview/schemabinding/version2/descriptors/JalviewUserColoursDescriptor.java @@ -72,6 +72,7 @@ public class JalviewUserColoursDescriptor extends desc.setImmutable(true); handler = new org.exolab.castor.xml.XMLFieldHandler() { + @Override public java.lang.Object getValue(java.lang.Object object) throws IllegalStateException { @@ -79,6 +80,7 @@ public class JalviewUserColoursDescriptor extends return target.getSchemeName(); } + @Override public void setValue(java.lang.Object object, java.lang.Object value) throws IllegalStateException, IllegalArgumentException { @@ -92,6 +94,7 @@ public class JalviewUserColoursDescriptor extends } } + @Override public java.lang.Object newInstance(java.lang.Object parent) { return null; @@ -119,6 +122,7 @@ public class JalviewUserColoursDescriptor extends desc.setImmutable(true); handler = new org.exolab.castor.xml.XMLFieldHandler() { + @Override public java.lang.Object getValue(java.lang.Object object) throws IllegalStateException { @@ -126,6 +130,7 @@ public class JalviewUserColoursDescriptor extends return target.getVersion(); } + @Override public void setValue(java.lang.Object object, java.lang.Object value) throws IllegalStateException, IllegalArgumentException { @@ -139,6 +144,7 @@ public class JalviewUserColoursDescriptor extends } } + @Override public java.lang.Object newInstance(java.lang.Object parent) { return null; @@ -163,6 +169,7 @@ public class JalviewUserColoursDescriptor extends org.exolab.castor.xml.NodeType.Element); handler = new org.exolab.castor.xml.XMLFieldHandler() { + @Override public java.lang.Object getValue(java.lang.Object object) throws IllegalStateException { @@ -170,6 +177,7 @@ public class JalviewUserColoursDescriptor extends return target.getColour(); } + @Override public void setValue(java.lang.Object object, java.lang.Object value) throws IllegalStateException, IllegalArgumentException { @@ -183,6 +191,7 @@ public class JalviewUserColoursDescriptor extends } } + @Override public void resetValue(Object object) throws IllegalStateException, IllegalArgumentException { @@ -196,6 +205,7 @@ public class JalviewUserColoursDescriptor extends } } + @Override public java.lang.Object newInstance(java.lang.Object parent) { return new Colour(); @@ -222,6 +232,7 @@ public class JalviewUserColoursDescriptor extends * * @return the access mode specified for this class. */ + @Override public org.exolab.castor.mapping.AccessMode getAccessMode() { return null; @@ -232,6 +243,7 @@ public class JalviewUserColoursDescriptor extends * * @return the identity field, null if this class has no identity. */ + @Override public org.exolab.castor.mapping.FieldDescriptor getIdentity() { return super.getIdentity(); @@ -242,6 +254,7 @@ public class JalviewUserColoursDescriptor extends * * @return the Java class represented by this descriptor. */ + @Override public java.lang.Class getJavaClass() { return jalview.schemabinding.version2.JalviewUserColours.class; @@ -252,6 +265,7 @@ public class JalviewUserColoursDescriptor extends * * @return the namespace prefix to use when marshaling as XML. */ + @Override public java.lang.String getNameSpacePrefix() { return _nsPrefix; @@ -262,6 +276,7 @@ public class JalviewUserColoursDescriptor extends * * @return the namespace URI used when marshaling and unmarshaling as XML. */ + @Override public java.lang.String getNameSpaceURI() { return _nsURI; @@ -273,6 +288,7 @@ public class JalviewUserColoursDescriptor extends * @return a specific validator for the class described by this * ClassDescriptor. */ + @Override public org.exolab.castor.xml.TypeValidator getValidator() { return this; @@ -283,6 +299,7 @@ public class JalviewUserColoursDescriptor extends * * @return the XML Name for the Class being described. */ + @Override public java.lang.String getXMLName() { return _xmlName; @@ -294,6 +311,7 @@ public class JalviewUserColoursDescriptor extends * @return true if XML schema definition of this Class is that of a global * element or element with anonymous type definition. */ + @Override public boolean isElementDefinition() { return _elementDefinition; diff --git a/src/jalview/schemabinding/version2/descriptors/UserColourSchemeDescriptor.java b/src/jalview/schemabinding/version2/descriptors/UserColourSchemeDescriptor.java index ece728a..df9ab07 100644 --- a/src/jalview/schemabinding/version2/descriptors/UserColourSchemeDescriptor.java +++ b/src/jalview/schemabinding/version2/descriptors/UserColourSchemeDescriptor.java @@ -11,6 +11,8 @@ package jalview.schemabinding.version2.descriptors; //- Imported classes and packages -/ //---------------------------------/ +import jalview.schemabinding.version2.UserColourScheme; + /** * Class UserColourSchemeDescriptor. * diff --git a/src/jalview/schemabinding/version2/descriptors/VamsasModelDescriptor.java b/src/jalview/schemabinding/version2/descriptors/VamsasModelDescriptor.java index 86e6992..3e26611 100644 --- a/src/jalview/schemabinding/version2/descriptors/VamsasModelDescriptor.java +++ b/src/jalview/schemabinding/version2/descriptors/VamsasModelDescriptor.java @@ -11,6 +11,8 @@ package jalview.schemabinding.version2.descriptors; //- Imported classes and packages -/ //---------------------------------/ +import jalview.schemabinding.version2.VamsasModel; + /** * Class VamsasModelDescriptor. * diff --git a/src/jalview/schemes/FeatureColour.java b/src/jalview/schemes/FeatureColour.java index 37c85d4..d0babf4 100644 --- a/src/jalview/schemes/FeatureColour.java +++ b/src/jalview/schemes/FeatureColour.java @@ -112,10 +112,10 @@ public class FeatureColour implements FeatureColourI } /* - * autoScaled == true: colours range over actual score range; autoScaled == - * false ('abso'): colours range over min/max range + * autoScaled == true: colours range over actual score range; + * autoScaled == false ('abso'): colours range over min/max range */ - boolean autoScaled = false; + boolean autoScaled = true; String tok = null, minval, maxval; if (mincol != null) { @@ -139,15 +139,15 @@ public class FeatureColour implements FeatureColourI } tok = gcol.nextToken(); gcol.nextToken(); // skip next '|' - if (tok.toLowerCase().indexOf("abso") != 0) + if (tok.toLowerCase().startsWith("abso")) { - minval = tok; - autoScaled = true; + minval = gcol.nextToken(); + gcol.nextToken(); // skip next '|' + autoScaled = false; } else { - minval = gcol.nextToken(); - gcol.nextToken(); // skip next '|' + minval = tok; } maxval = gcol.nextToken(); if (gcol.hasMoreTokens()) @@ -365,20 +365,6 @@ public class FeatureColour implements FeatureColourI return graduatedColour; } - /** - * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to - * false. - */ - @Override - public void setGraduatedColour(boolean b) - { - graduatedColour = b; - if (b) - { - setColourByLabel(false); - } - } - @Override public ColorI getColour() { @@ -416,6 +402,20 @@ public class FeatureColour implements FeatureColourI setGraduatedColour(false); } } + + /** + * Sets the 'graduated colour' flag. If true, also sets 'colour by label' to + * false. + */ + void setGraduatedColour(boolean b) + { + graduatedColour = b; + if (b) + { + setColourByLabel(false); + } + } + @Override public boolean isBelowThreshold() { @@ -654,7 +654,7 @@ public class FeatureColour implements FeatureColourI { sb.append(Format.getHexString(getMinColour())).append(BAR); sb.append(Format.getHexString(getMaxColour())).append(BAR); - if (isAutoScaled()) + if (!isAutoScaled()) { sb.append("abso").append(BAR); } diff --git a/src/jalview/schemes/FeatureColourAdapter.java b/src/jalview/schemes/FeatureColourAdapter.java index cc03dd3..869a8ca 100644 --- a/src/jalview/schemes/FeatureColourAdapter.java +++ b/src/jalview/schemes/FeatureColourAdapter.java @@ -147,9 +147,4 @@ public class FeatureColourAdapter implements FeatureColourI { } - @Override - public void setGraduatedColour(boolean b) - { - } - } diff --git a/src/jalview/schemes/GraduatedColor.java b/src/jalview/schemes/GraduatedColor.java deleted file mode 100644 index 2d1c572..0000000 --- a/src/jalview/schemes/GraduatedColor.java +++ /dev/null @@ -1,304 +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 . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ -package jalview.schemes; - -import jalview.api.FeatureColourI; -import jalview.datamodel.SequenceFeature; - -import java.awt.Color; - -/** - * Value and/or thresholded colour scale used for colouring by annotation and - * feature score - * - * @author JimP - * - */ -public class GraduatedColor -{ - int thresholdState = AnnotationColourGradient.NO_THRESHOLD; // or - // ABOVE_THRESHOLD - // or - // BELOW_THRESHOLD - - float lr, lg, lb, dr, dg, db; - - /** - * linear scaling parameters, base, minimum colour threshold, range of linear - * scale from lower to upper - */ - float base, range, thrsh; - - /** - * when true, colour from u to u-d rather than u to u+d - */ - boolean tolow = false; - - /** - * when false, min/max range has been manually set so should not be - * dynamically adjusted. - */ - boolean autoScale = true; - - /** - * construct a graduatedColor object from simple parameters - * - * @param low - * @param high - * @param min - * @param max - * color low->high from min->max - */ - public GraduatedColor(Color low, Color high, float min, float max) - { - thrsh = Float.NaN; - tolow = min >= max; - lr = low.getRed() / 255f; - lg = low.getGreen() / 255f; - lb = low.getBlue() / 255f; - dr = (high.getRed() / 255f) - lr; - dg = (high.getGreen() / 255f) - lg; - db = (high.getBlue() / 255f) - lb; - if (tolow) - { - base = max; - range = min - max; - } - else - { - base = min; - range = max - min; - } - } - - public GraduatedColor(GraduatedColor oldcs) - { - lr = oldcs.lr; - lg = oldcs.lg; - lb = oldcs.lb; - dr = oldcs.dr; - dg = oldcs.dg; - db = oldcs.db; - base = oldcs.base; - range = oldcs.range; - tolow = oldcs.tolow; - thresholdState = oldcs.thresholdState; - thrsh = oldcs.thrsh; - autoScale = oldcs.autoScale; - colourByLabel = oldcs.colourByLabel; - } - - /** - * make a new gradient from an old one with a different scale range - * - * @param oldcs - * @param min - * @param max - */ - public GraduatedColor(GraduatedColor oldcs, float min, float max) - { - this(oldcs); - updateBounds(min, max); - } - - public GraduatedColor(FeatureColourI col) - { - setColourByLabel(col.isColourByLabel()); - } - - public Color getMinColor() - { - return new Color(lr, lg, lb); - } - - public Color getMaxColor() - { - return new Color(lr + dr, lg + dg, lb + db); - } - - /** - * - * @return true if original min/max scale was from high to low - */ - public boolean getTolow() - { - return tolow; - } - - public void setTolow(boolean tolower) - { - tolow = tolower; - } - - public boolean isColored(SequenceFeature feature) - { - float val = feature.getScore(); - if (Float.isNaN(val)) - { - return true; - } - if (this.thresholdState == AnnotationColourGradient.NO_THRESHOLD) - { - return true; - } - if (Float.isNaN(this.thrsh)) - { - return true; - } - boolean rtn = thresholdState == AnnotationColourGradient.ABOVE_THRESHOLD; - if (val <= thrsh) - { - return !rtn; // ? !tolow : tolow; - } - else - { - return rtn; // ? tolow : !tolow; - } - } - - /** - * default implementor of a getColourFromString method. TODO: abstract an - * interface enabling pluggable colour from string - */ - private UserColourScheme ucs = null; - - private boolean colourByLabel = false; - - /** - * - * @return true if colourByLabel style is set - */ - public boolean isColourByLabel() - { - return colourByLabel; - } - - /** - * @param colourByLabel - * the colourByLabel to set - */ - public void setColourByLabel(boolean colourByLabel) - { - this.colourByLabel = colourByLabel; - } - - public Color findColor(SequenceFeature feature) - { - if (colourByLabel) - { - // TODO: allow user defined feature label colourschemes. Colour space is - // {type,regex,%anytype%}x{description string, regex, keyword} - if (ucs == null) - { - ucs = new UserColourScheme(); - } - return ucs.createColourFromName(feature.getDescription()); - } - if (range == 0.0) - { - return getMaxColor(); - } - float scr = feature.getScore(); - if (Float.isNaN(scr)) - { - return getMinColor(); - } - float scl = (scr - base) / range; - if (tolow) - { - scl = -scl; - } - if (scl < 0f) - { - scl = 0f; - } - if (scl > 1f) - { - scl = 1f; - } - return new Color(lr + scl * dr, lg + scl * dg, lb + scl * db); - } - - public void setThresh(float value) - { - thrsh = value; - } - - public float getThresh() - { - return thrsh; - } - - public void setThreshType(int aboveThreshold) - { - thresholdState = aboveThreshold; - } - - public int getThreshType() - { - return thresholdState; - } - - public float getMax() - { - // regenerate the original values passed in to the constructor - return (tolow) ? base : (base + range); - } - - public float getMin() - { - // regenerate the original value passed in to the constructor - return (tolow) ? (base + range) : base; - } - - public boolean isAutoScale() - { - return autoScale; - } - - public void setAutoScaled(boolean autoscale) - { - autoScale = autoscale; - } - - /** - * update the base and range appropriatly for the given minmax range - * - * @param a - * float[] {min,max} array containing minmax range for the associated - * score values - */ - public void updateBounds(float min, float max) - { - if (max < min) - { - base = max; - range = min - max; - tolow = true; - } - else - { - base = min; - range = max - min; - tolow = false; - } - } -} diff --git a/src/jalview/schemes/RNAHelicesColour.java b/src/jalview/schemes/RNAHelicesColour.java index d17f510..9729a83 100644 --- a/src/jalview/schemes/RNAHelicesColour.java +++ b/src/jalview/schemes/RNAHelicesColour.java @@ -91,6 +91,10 @@ public class RNAHelicesColour extends ResidueColourScheme // This loop will find the first rna structure annotation by which to colour // the sequences. AlignmentAnnotation[] annotations = alignment.getAlignmentAnnotation(); + if (annotations == null) + { + return; + } for (int i = 0; i < annotations.length; i++) { diff --git a/src/jalview/schemes/RNAHelicesColourChooser.java b/src/jalview/schemes/RNAHelicesColourChooser.java index 1a13bbe..b1b3c5a 100644 --- a/src/jalview/schemes/RNAHelicesColourChooser.java +++ b/src/jalview/schemes/RNAHelicesColourChooser.java @@ -22,6 +22,7 @@ package jalview.schemes; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; +import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.SequenceGroup; import java.awt.event.ActionEvent; @@ -77,16 +78,20 @@ public class RNAHelicesColourChooser adjusting = true; Vector list = new Vector(); int index = 1; - for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++) + AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation(); + if (anns != null) { - String label = av.getAlignment().getAlignmentAnnotation()[i].label; - if (!list.contains(label)) + for (int i = 0; i < anns.length; i++) { - list.addElement(label); - } - else - { - list.addElement(label + "_" + (index++)); + String label = anns[i].label; + if (!list.contains(label)) + { + list.addElement(label); + } + else + { + list.addElement(label + "_" + (index++)); + } } } diff --git a/src/jalview/schemes/ResidueProperties.java b/src/jalview/schemes/ResidueProperties.java index 2aa24a1..d0d26b0 100755 --- a/src/jalview/schemes/ResidueProperties.java +++ b/src/jalview/schemes/ResidueProperties.java @@ -1603,87 +1603,6 @@ public class ResidueProperties return ss.toString(); } - /** - * Used by getRNASecStrucState - * - */ - public static Hashtable toRNAssState; - - public static boolean RNAcloseParen[] = new boolean[255]; - static - { - toRNAssState = new Hashtable(); - toRNAssState.put(")", "("); - toRNAssState.put("(", "("); - toRNAssState.put("]", "["); - toRNAssState.put("[", "["); - toRNAssState.put("{", "{"); - toRNAssState.put("}", "{"); - toRNAssState.put(">", ">"); - toRNAssState.put("<", ">"); - toRNAssState.put("A", "A"); - toRNAssState.put("a", "A"); - toRNAssState.put("B", "B"); - toRNAssState.put("b", "B"); - toRNAssState.put("C", "C"); - toRNAssState.put("c", "C"); - toRNAssState.put("D", "D"); - toRNAssState.put("d", "D"); - toRNAssState.put("E", "E"); - toRNAssState.put("e", "E"); - toRNAssState.put("F", "F"); - toRNAssState.put("f", "F"); - toRNAssState.put("G", "G"); - toRNAssState.put("g", "G"); - toRNAssState.put("H", "H"); - toRNAssState.put("h", "H"); - toRNAssState.put("I", "I"); - toRNAssState.put("i", "I"); - toRNAssState.put("J", "J"); - toRNAssState.put("j", "J"); - toRNAssState.put("K", "K"); - toRNAssState.put("k", "K"); - toRNAssState.put("L", "L"); - toRNAssState.put("l", "L"); - toRNAssState.put("M", "M"); - toRNAssState.put("m", "M"); - toRNAssState.put("N", "N"); - toRNAssState.put("n", "N"); - toRNAssState.put("O", "O"); - toRNAssState.put("o", "O"); - toRNAssState.put("P", "P"); - toRNAssState.put("p", "P"); - toRNAssState.put("Q", "Q"); - toRNAssState.put("q", "Q"); - toRNAssState.put("R", "R"); - toRNAssState.put("r", "R"); - toRNAssState.put("S", "S"); - toRNAssState.put("s", "S"); - toRNAssState.put("T", "T"); - toRNAssState.put("t", "T"); - toRNAssState.put("U", "U"); - toRNAssState.put("u", "U"); - toRNAssState.put("V", "V"); - toRNAssState.put("v", "V"); - toRNAssState.put("W", "W"); - toRNAssState.put("w", "W"); - toRNAssState.put("X", "X"); - toRNAssState.put("x", "X"); - toRNAssState.put("Y", "Y"); - toRNAssState.put("y", "Y"); - toRNAssState.put("Z", "Z"); - toRNAssState.put("z", "Z"); - for (int p = 0; p < RNAcloseParen.length; p++) - { - RNAcloseParen[p] = false; - } - for (String k : toRNAssState.keySet()) - { - RNAcloseParen[k.charAt(0)] = k.charAt(0) != toRNAssState.get(k) - .charAt(0); - } - } - static { modifications.put("MSE", "MET"); // Selenomethionine @@ -3008,40 +2927,6 @@ public class ResidueProperties return canonical == null ? aa : canonical; } - /** - * translate to RNA secondary structure representation - * - * @param ssstring - * @return ssstring as a RNA-state secondary structure assignment. - */ - public static String getRNASecStrucState(String ssstring) - { - if (ssstring == null) - { - return null; - } - StringBuffer ss = new StringBuffer(); - for (int i = 0; i < ssstring.length(); i++) - { - String ssc = ssstring.substring(i, i + 1); - if (toRNAssState.containsKey(ssc)) - { - // valid ss character - so return it - ss.append(ssc); // (String) toRNAssState.get(ssc)); - } - else - { - ss.append(" "); - } - } - return ss.toString(); - } - - public static boolean isCloseParenRNA(char dc) - { - return RNAcloseParen[dc]; - } - // main method generates perl representation of residue property hash // / cut here public static void main(String[] args) diff --git a/src/jalview/schemes/UserColourScheme.java b/src/jalview/schemes/UserColourScheme.java index 2bc2d26..9ae14ca 100755 --- a/src/jalview/schemes/UserColourScheme.java +++ b/src/jalview/schemes/UserColourScheme.java @@ -101,6 +101,10 @@ public class UserColourScheme extends ResidueColourScheme public static Color getColourFromString(String colour) { + if (colour == null) + { + return null; + } colour = colour.trim(); Color col = null; diff --git a/src/jalview/structure/StructureImportSettings.java b/src/jalview/structure/StructureImportSettings.java new file mode 100644 index 0000000..82b5f69 --- /dev/null +++ b/src/jalview/structure/StructureImportSettings.java @@ -0,0 +1,133 @@ +package jalview.structure; + +import jalview.datamodel.PDBEntry; +import jalview.datamodel.PDBEntry.Type; + +/** + * bean holding settings for structure IO. TODO: tests for validation of values + * TODO: tests for race conditions (all fields are static, is that correct ?) + * + * @author tcofoegbu + * + */ +public class StructureImportSettings +{ + /** + * set to true to add derived sequence annotations (temp factor read from + * file, or computed secondary structure) to the alignment + */ + private static boolean visibleChainAnnotation = false; + + /** + * Set true to predict secondary structure (using JMol for protein, Annotate3D + * for RNA) + */ + private static boolean processSecStr = false; + + /** + * Set true (with predictSecondaryStructure=true) to predict secondary + * structure using an external service (currently Annotate3D for RNA only) + */ + private static boolean externalSecondaryStructure = false; + + private static boolean showSeqFeatures = true; + + public enum StructureParser + { + JMOL_PARSER, JALVIEW_PARSER + } + + + /** + * Determines the default file format for structure files to be downloaded + * from the PDB sequence fetcher. Possible options include: PDB|mmCIF + */ + private static PDBEntry.Type defaultStructureFileFormat = Type.PDB; + + /** + * Determines the parser used for parsing PDB format file. Possible options + * are : JMolParser|JalveiwParser + */ + private static StructureParser defaultPDBFileParser = StructureParser.JMOL_PARSER; + public static void addSettings(boolean addAlignmentAnnotations, + boolean processSecStr, boolean externalSecStr) + { + StructureImportSettings.visibleChainAnnotation = addAlignmentAnnotations; + StructureImportSettings.processSecStr = processSecStr; + StructureImportSettings.externalSecondaryStructure = externalSecStr; + StructureImportSettings.showSeqFeatures = true; + } + + public static boolean isVisibleChainAnnotation() + { + return visibleChainAnnotation; + } + + public static void setVisibleChainAnnotation( + boolean visibleChainAnnotation) + { + StructureImportSettings.visibleChainAnnotation = visibleChainAnnotation; + } + + public static boolean isProcessSecondaryStructure() + { + return processSecStr; + } + + public static void setProcessSecondaryStructure( + boolean processSecondaryStructure) + { + StructureImportSettings.processSecStr = processSecondaryStructure; + } + + public static boolean isExternalSecondaryStructure() + { + return externalSecondaryStructure; + } + + public static void setExternalSecondaryStructure( + boolean externalSecondaryStructure) + { + StructureImportSettings.externalSecondaryStructure = externalSecondaryStructure; + } + + public static boolean isShowSeqFeatures() + { + return showSeqFeatures; + } + + public static void setShowSeqFeatures(boolean showSeqFeatures) + { + StructureImportSettings.showSeqFeatures = showSeqFeatures; + } + + public static String getDefaultStructureFileFormat() + { + return defaultStructureFileFormat.toString(); + } + + public static void setDefaultStructureFileFormat( + String defaultStructureFileFormat) + { + StructureImportSettings.defaultStructureFileFormat = PDBEntry.Type + .valueOf(defaultStructureFileFormat.toUpperCase()); + } + + public static String getDefaultPDBFileParser() + { + return defaultPDBFileParser.toString(); + } + + public static void setDefaultPDBFileParser( + StructureParser defaultPDBFileParser) + { + StructureImportSettings.defaultPDBFileParser = defaultPDBFileParser; + } + + public static void setDefaultPDBFileParser(String defaultPDBFileParser) + { + StructureImportSettings.defaultPDBFileParser = StructureParser + .valueOf(defaultPDBFileParser.toUpperCase()); + } + +} diff --git a/src/jalview/structure/StructureMapping.java b/src/jalview/structure/StructureMapping.java index 3fcb5e9..78634e0 100644 --- a/src/jalview/structure/StructureMapping.java +++ b/src/jalview/structure/StructureMapping.java @@ -159,4 +159,14 @@ public class StructureMapping } return ala_copy; } + + public String getMappingDetailsOutput() + { + return mappingDetails; + } + + public HashMap getMapping() + { + return mapping; + } } diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index a5d9736..7b103be 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -32,6 +32,7 @@ import jalview.datamodel.Annotation; import jalview.datamodel.PDBEntry; import jalview.datamodel.SearchResults; import jalview.datamodel.SequenceI; +import jalview.ext.jmol.JmolParser; import jalview.gui.IProgressIndicator; import jalview.io.AppletFormatAdapter; import jalview.io.StructureFile; @@ -384,17 +385,7 @@ public class StructureSelectionManager boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts(); try { - - if (pdbFile != null && isCIFFile(pdbFile)) - { - pdb = new jalview.ext.jmol.JmolParser(addTempFacAnnot, parseSecStr, - secStructServices, pdbFile, protocol); - } - else - { - pdb = new PDBfile(addTempFacAnnot, parseSecStr, secStructServices, - pdbFile, protocol); - } + pdb = new JmolParser(pdbFile, protocol); if (pdb.getId() != null && pdb.getId().trim().length() > 0 && AppletFormatAdapter.FILE.equals(protocol)) @@ -424,6 +415,12 @@ public class StructureSelectionManager { boolean infChain = true; final SequenceI seq = sequenceArray[s]; + SequenceI ds = seq; + while (ds.getDatasetSequence() != null) + { + ds = ds.getDatasetSequence(); + } + if (targetChainIds != null && targetChainIds[s] != null) { infChain = false; @@ -498,34 +495,89 @@ public class StructureSelectionManager } ArrayList seqToStrucMapping = new ArrayList(); - if (isMapUsingSIFTs) + if (isMapUsingSIFTs && seq.isProtein()) { setProgressBar(null); - setProgressBar("Obtaining mapping with SIFTS"); + setProgressBar(MessageManager + .getString("status.obtaining_mapping_with_sifts")); jalview.datamodel.Mapping sqmpping = maxAlignseq .getMappingFromS1(false); if (targetChainId != null && !targetChainId.trim().isEmpty()) { - StructureMapping mapping = getStructureMapping(seq, pdbFile, - targetChainId, pdb, maxChain, sqmpping, maxAlignseq); - seqToStrucMapping.add(mapping); + StructureMapping siftsMapping; + try + { + siftsMapping = getStructureMapping(seq, pdbFile, targetChainId, + pdb, maxChain, sqmpping, maxAlignseq); + seqToStrucMapping.add(siftsMapping); + maxChain.makeExactMapping(maxAlignseq, seq); + maxChain.transferRESNUMFeatures(seq, null);// FIXME: is this + // "IEA:SIFTS" ? + maxChain.transferResidueAnnotation(siftsMapping, sqmpping); + ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); + + } catch (SiftsException e) + { + // fall back to NW alignment + System.err.println(e.getMessage()); + StructureMapping nwMapping = getNWMappings(seq, pdbFile, + targetChainId, maxChain, pdb, maxAlignseq); + seqToStrucMapping.add(nwMapping); + maxChain.makeExactMapping(maxAlignseq, seq); + maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this + // "IEA:Jalview" ? + maxChain.transferResidueAnnotation(nwMapping, sqmpping); + ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); + } } else { + ArrayList foundSiftsMappings = new ArrayList(); for (PDBChain chain : pdb.getChains()) { - StructureMapping mapping = getStructureMapping(seq, pdbFile, - chain.id, pdb, chain, sqmpping, maxAlignseq); - seqToStrucMapping.add(mapping); + try + { + StructureMapping siftsMapping = getStructureMapping(seq, + pdbFile, + chain.id, pdb, chain, sqmpping, maxAlignseq); + foundSiftsMappings.add(siftsMapping); + } catch (SiftsException e) + { + System.err.println(e.getMessage()); + } + } + if (!foundSiftsMappings.isEmpty()) + { + seqToStrucMapping.addAll(foundSiftsMappings); + maxChain.makeExactMapping(maxAlignseq, seq); + maxChain.transferRESNUMFeatures(seq, null);// FIXME: is this + // "IEA:SIFTS" ? + maxChain.transferResidueAnnotation(foundSiftsMappings.get(0), + sqmpping); + ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0)); + } + else + { + StructureMapping nwMapping = getNWMappings(seq, pdbFile, + maxChainId, maxChain, pdb, maxAlignseq); + seqToStrucMapping.add(nwMapping); + maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this + // "IEA:Jalview" ? + maxChain.transferResidueAnnotation(nwMapping, sqmpping); + ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); } } } else { setProgressBar(null); - setProgressBar("Obtaining mapping with NW alignment"); - seqToStrucMapping.add(getNWMappings(seq, pdbFile, maxChainId, - maxChain, pdb, maxAlignseq)); + setProgressBar(MessageManager + .getString("status.obtaining_mapping_with_nw_alignment")); + StructureMapping nwMapping = getNWMappings(seq, pdbFile, + maxChainId, maxChain, pdb, maxAlignseq); + seqToStrucMapping.add(nwMapping); + ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); + } if (forStructureView) @@ -543,14 +595,25 @@ public class StructureSelectionManager return "cif".equalsIgnoreCase(fileExt); } + /** + * retrieve a mapping for seq from SIFTs using associated DBRefEntry for + * uniprot or PDB + * + * @param seq + * @param pdbFile + * @param targetChainId + * @param pdb + * @param maxChain + * @param sqmpping + * @param maxAlignseq + * @return + * @throws SiftsException + */ private StructureMapping getStructureMapping(SequenceI seq, String pdbFile, String targetChainId, StructureFile pdb, PDBChain maxChain, jalview.datamodel.Mapping sqmpping, - AlignSeq maxAlignseq) + AlignSeq maxAlignseq) throws SiftsException { - String maxChainId = targetChainId; - try - { StructureMapping curChainMapping = siftsClient .getSiftsStructureMapping(seq, pdbFile, targetChainId); try @@ -565,15 +628,6 @@ public class StructureSelectionManager e.printStackTrace(); } return curChainMapping; - } catch (SiftsException e) - { - System.err.println(e.getMessage()); - System.err.println(">>> Now switching mapping with NW alignment..."); - setProgressBar(null); - setProgressBar(">>> Now switching mapping with NW alignment..."); - return getNWMappings(seq, pdbFile, maxChainId, maxChain, pdb, - maxAlignseq); - } } private StructureMapping getNWMappings(SequenceI seq, diff --git a/src/jalview/structure/StructureViewSettings.java b/src/jalview/structure/StructureViewSettings.java deleted file mode 100644 index 2fcb3e5..0000000 --- a/src/jalview/structure/StructureViewSettings.java +++ /dev/null @@ -1,79 +0,0 @@ -package jalview.structure; - -public class StructureViewSettings -{ - private String dbRefType; - - /** - * set to true to add derived sequence annotations (temp factor read from - * file, or computed secondary structure) to the alignment - */ - private static boolean visibleChainAnnotation = false; - - /** - * Set true to predict secondary structure (using JMol for protein, Annotate3D - * for RNA) - */ - private static boolean predictSecStr = false; - - /** - * Set true (with predictSecondaryStructure=true) to predict secondary - * structure using an external service (currently Annotate3D for RNA only) - */ - private static boolean externalSecondaryStructure = false; - - private static boolean showSeqFeatures = true; - - public static void addSettings(boolean addAlignmentAnnotations, - boolean predictSecStr, boolean externalSecStr) - { - StructureViewSettings.visibleChainAnnotation = addAlignmentAnnotations; - StructureViewSettings.predictSecStr = predictSecStr; - StructureViewSettings.externalSecondaryStructure = externalSecStr; - StructureViewSettings.showSeqFeatures = true; - } - - public static boolean isVisibleChainAnnotation() - { - return visibleChainAnnotation; - } - - public static void setVisibleChainAnnotation( - boolean visibleChainAnnotation) - { - StructureViewSettings.visibleChainAnnotation = visibleChainAnnotation; - } - - public static boolean isPredictSecondaryStructure() - { - return predictSecStr; - } - - public static void setPredictSecondaryStructure( - boolean predictSecondaryStructure) - { - StructureViewSettings.predictSecStr = predictSecondaryStructure; - } - - public static boolean isExternalSecondaryStructure() - { - return externalSecondaryStructure; - } - - public static void setExternalSecondaryStructure( - boolean externalSecondaryStructure) - { - StructureViewSettings.externalSecondaryStructure = externalSecondaryStructure; - } - - public static boolean isShowSeqFeatures() - { - return showSeqFeatures; - } - - public static void setShowSeqFeatures(boolean showSeqFeatures) - { - StructureViewSettings.showSeqFeatures = showSeqFeatures; - } - -} diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index 42fbfa9..dc42315 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -598,6 +598,10 @@ public abstract class AAStructureBindingModel extends for (String file : files) { notLoaded = file; + if (file == null) + { + continue; + } try { StructureMapping[] sm = getSsm().getMapping(file); diff --git a/src/jalview/util/CaseInsensitiveString.java b/src/jalview/util/CaseInsensitiveString.java new file mode 100644 index 0000000..aa42228 --- /dev/null +++ b/src/jalview/util/CaseInsensitiveString.java @@ -0,0 +1,57 @@ +package jalview.util; + +/** + * A class to wrap a case insensitive string. For use in collections where we + * want to preserve case, but do not want to duplicate upper and lower case + * variants + */ +public final class CaseInsensitiveString +{ + String value; + + public CaseInsensitiveString(String s) + { + this.value = s; + } + + @Override + public String toString() + { + return value; + } + + /** + * Answers true if the object compared to is a CaseInsensitiveString wrapping + * the same string value (ignoring case), or if both wrap a null value, else + * false + */ + @Override + public boolean equals(Object o) + { + if (o == null) + { + return false; + } + if (!(o instanceof CaseInsensitiveString)) + { + return false; + } + CaseInsensitiveString obj = (CaseInsensitiveString) o; + if (value == null) + { + return obj.value == null; + } + return value.equalsIgnoreCase(obj.value); + } + + /** + * hashCode overriden to guarantee that 'equal' objects have the same hash + * code + */ + @Override + public int hashCode() + { + return value == null ? super.hashCode() : value.toUpperCase() + .hashCode(); + } +} diff --git a/src/jalview/util/Comparison.java b/src/jalview/util/Comparison.java index 5605a53..0beb45b 100644 --- a/src/jalview/util/Comparison.java +++ b/src/jalview/util/Comparison.java @@ -249,6 +249,18 @@ public class Comparison } /** + * Overloaded method signature to test whether a single sequence is nucleotide + * (that is, more than 85% CGTA) + * + * @param seq + * @return + */ + public static final boolean isNucleotide(SequenceI seq) + { + return isNucleotide(new SequenceI[] { seq }); + } + + /** * Answers true if more than 85% of the sequence residues (ignoring gaps) are * A, G, C, T or U, else false. This is just a heuristic guess and may give a * wrong answer (as AGCT are also amino acid codes). diff --git a/src/jalview/util/DBRefUtils.java b/src/jalview/util/DBRefUtils.java index 424d40b..405f6e6 100755 --- a/src/jalview/util/DBRefUtils.java +++ b/src/jalview/util/DBRefUtils.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.Set; import com.stevesoft.pat.Regex; @@ -59,6 +60,18 @@ public class DBRefUtils canonicalSourceNameLookup.put("pdb", DBRefSource.PDB); canonicalSourceNameLookup.put("ensembl", DBRefSource.ENSEMBL); + // Ensembl Gn and Tr are for Ensembl genomic and transcript IDs as served + // from ENA. + canonicalSourceNameLookup.put("ensembl-tr", DBRefSource.ENSEMBL); + canonicalSourceNameLookup.put("ensembl-gn", DBRefSource.ENSEMBL); + + // Make sure we have lowercase entries for all canonical string lookups + Set keys = canonicalSourceNameLookup.keySet(); + for (String k : keys) + { + canonicalSourceNameLookup.put(k.toLowerCase(), + canonicalSourceNameLookup.get(k)); + } dasCoordinateSystemsLookup.put("pdbresnum", DBRefSource.PDB); dasCoordinateSystemsLookup.put("uniprot", DBRefSource.UNIPROT); @@ -67,11 +80,14 @@ public class DBRefUtils } /** + * Returns those DBRefEntry objects whose source identifier (once converted to + * Jalview's canonical form) is in the list of sources to search for. Returns + * null if no matches found. * * @param dbrefs - * array of DBRef objects to search + * DBRefEntry objects to search * @param sources - * String[] array of source DBRef IDs to retrieve + * array of sources to select * @return */ public static DBRefEntry[] selectRefs(DBRefEntry[] dbrefs, @@ -148,8 +164,8 @@ public class DBRefUtils } /** - * Returns an array of those references that match the given entry, or null if - * no matches. Currently uses a comparator which matches if + * Returns a (possibly empty) list of those references that match the given + * entry. Currently uses a comparator which matches if *
          *
        • database sources are the same
        • *
        • accession ids are the same
        • @@ -162,34 +178,35 @@ public class DBRefUtils * pattern to match * @return */ - public static DBRefEntry[] searchRefs(DBRefEntry[] ref, DBRefEntry entry) + public static List searchRefs(DBRefEntry[] ref, + DBRefEntry entry) { return searchRefs(ref, entry, matchDbAndIdAndEitherMapOrEquivalentMapList); } /** - * Returns an array of those references that match the given accession id + * Returns a list of those references that match the given accession id *
            *
          • database sources are the same
          • *
          • accession ids are the same
          • *
          • both have no mapping, or the mappings are the same
          • *
          * - * @param ref + * @param refs * Set of references to search - * @param entry - * pattern to match + * @param accId + * accession id to match * @return */ - public static DBRefEntry[] searchRefs(DBRefEntry[] ref, String accId) + public static List searchRefs(DBRefEntry[] refs, String accId) { - return searchRefs(ref, new DBRefEntry("", "", accId), matchId); + return searchRefs(refs, new DBRefEntry("", "", accId), matchId); } /** - * Returns an array of those references that match the given entry, according - * to the given comparator. Returns null if no matches. + * Returns a (possibly empty) list of those references that match the given + * entry, according to the given comparator. * * @param refs * an array of database references to search @@ -198,14 +215,14 @@ public class DBRefUtils * @param comparator * @return */ - static DBRefEntry[] searchRefs(DBRefEntry[] refs, DBRefEntry entry, + static List searchRefs(DBRefEntry[] refs, DBRefEntry entry, DbRefComp comparator) { + List rfs = new ArrayList(); if (refs == null || entry == null) { - return null; + return rfs; } - List rfs = new ArrayList(); for (int i = 0; i < refs.length; i++) { if (comparator.matches(entry, refs[i])) @@ -213,7 +230,7 @@ public class DBRefUtils rfs.add(refs[i]); } } - return rfs.size() == 0 ? null : rfs.toArray(new DBRefEntry[rfs.size()]); + return rfs; } interface DbRefComp @@ -231,7 +248,8 @@ public class DBRefUtils public boolean matches(DBRefEntry refa, DBRefEntry refb) { if (refa.getSource() == null - || refb.getSource().equals(refa.getSource())) + || DBRefUtils.getCanonicalName(refb.getSource()).equals( + DBRefUtils.getCanonicalName(refa.getSource()))) { if (refa.getVersion() == null || refb.getVersion().equals(refa.getVersion())) @@ -262,7 +280,7 @@ public class DBRefUtils @Override public boolean matches(DBRefEntry refa, DBRefEntry refb) { - if (nullOrEqual(refa.getSource(), refb.getSource()) + if (nullOrEqualSource(refa.getSource(), refb.getSource()) && nullOrEqual(refa.getVersion(), refb.getVersion()) && nullOrEqual(refa.getAccessionId(), refb.getAccessionId()) && nullOrEqual(refa.getMap(), refb.getMap())) @@ -284,7 +302,8 @@ public class DBRefUtils public boolean matches(DBRefEntry refa, DBRefEntry refb) { if (refa.getSource() != null && refb.getSource() != null - && refb.getSource().equals(refa.getSource())) + && DBRefUtils.getCanonicalName(refb.getSource()).equals( + DBRefUtils.getCanonicalName(refa.getSource()))) { // We dont care about version if (refa.getAccessionId() != null && refb.getAccessionId() != null @@ -315,7 +334,8 @@ public class DBRefUtils public boolean matches(DBRefEntry refa, DBRefEntry refb) { if (refa.getSource() != null && refb.getSource() != null - && refb.getSource().equals(refa.getSource())) + && DBRefUtils.getCanonicalName(refb.getSource()).equals( + DBRefUtils.getCanonicalName(refa.getSource()))) { // We dont care about version if (refa.getAccessionId() != null && refb.getAccessionId() != null @@ -351,7 +371,8 @@ public class DBRefUtils public boolean matches(DBRefEntry refa, DBRefEntry refb) { if (refa.getSource() != null && refb.getSource() != null - && refb.getSource().equals(refa.getSource())) + && DBRefUtils.getCanonicalName(refb.getSource()).equals( + DBRefUtils.getCanonicalName(refa.getSource()))) { // We dont care about version // if ((refa.getVersion()==null || refb.getVersion()==null) @@ -380,9 +401,9 @@ public class DBRefUtils }; /** - * accession ID and DB must be identical. Version is ignored. No map on either - * or map but no maplist on either or maplist of map on a is equivalent to the - * maplist of map on b. + * accession ID and DB must be identical, or null on a. Version is ignored. No + * map on either or map but no maplist on either or maplist of map on a is + * equivalent to the maplist of map on b. */ public static DbRefComp matchDbAndIdAndEitherMapOrEquivalentMapList = new DbRefComp() { @@ -390,11 +411,13 @@ public class DBRefUtils public boolean matches(DBRefEntry refa, DBRefEntry refb) { if (refa.getSource() != null && refb.getSource() != null - && refb.getSource().equals(refa.getSource())) + && DBRefUtils.getCanonicalName(refb.getSource()).equals( + DBRefUtils.getCanonicalName(refa.getSource()))) { // We dont care about version - if (refa.getAccessionId() != null && refb.getAccessionId() != null - && refb.getAccessionId().equals(refa.getAccessionId())) + + if (refa.getAccessionId() == null + || refa.getAccessionId().equals(refb.getAccessionId())) { if (refa.getMap() == null || refb.getMap() == null) { @@ -406,7 +429,7 @@ public class DBRefUtils || (refb.getMap().getMap() != null && refa.getMap().getMap() != null && (refb .getMap().getMap().equals(refa.getMap().getMap())))) - { // getMap().getMap().containsEither(false,refa.getMap().getMap()) + { return true; } } @@ -516,7 +539,73 @@ public class DBRefUtils { return true; } - return (o1 == null ? o2.equals(o1) : o1.equals(o2)); + return o1.equals(o2); + } + + /** + * canonicalise source string before comparing. null is always wildcard + * + * @param o1 + * - null or source string to compare + * @param o2 + * - null or source string to compare + * @return true if either o1 or o2 are null, or o1 equals o2 under + * DBRefUtils.getCanonicalName + * (o1).equals(DBRefUtils.getCanonicalName(o2)) + */ + public static boolean nullOrEqualSource(String o1, String o2) + { + if (o1 == null || o2 == null) + { + return true; + } + return DBRefUtils.getCanonicalName(o1).equals( + DBRefUtils.getCanonicalName(o2)); + } + + /** + * Selects just the DNA or protein references from a set of references + * + * @param selectDna + * if true, select references to 'standard' DNA databases, else to + * 'standard' peptide databases + * @param refs + * a set of references to select from + * @return + */ + public static DBRefEntry[] selectDbRefs(boolean selectDna, + DBRefEntry[] refs) + { + return selectRefs(refs, selectDna ? DBRefSource.DNACODINGDBS + : DBRefSource.PROTEINDBS); + // could attempt to find other cross + // refs here - ie PDB xrefs + // (not dna, not protein seq) + } + + /** + * Returns the (possibly empty) list of those supplied dbrefs which have the + * specified source database, with a case-insensitive match of source name + * + * @param dbRefs + * @param source + * @return + */ + public static List searchRefsForSource(DBRefEntry[] dbRefs, + String source) + { + List matches = new ArrayList(); + if (dbRefs != null && source != null) + { + for (DBRefEntry dbref : dbRefs) + { + if (source.equalsIgnoreCase(dbref.getSource())) + { + matches.add(dbref); + } + } + } + return matches; } } diff --git a/src/jalview/util/DnaUtils.java b/src/jalview/util/DnaUtils.java new file mode 100644 index 0000000..9582e2e --- /dev/null +++ b/src/jalview/util/DnaUtils.java @@ -0,0 +1,133 @@ +package jalview.util; + +import java.text.ParseException; +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. Throws an exception if not able to parse. + *

          + * Currently we do not parse "order()" specifiers, or indeterminate ranges of + * the format "<start..end" or "start..>end" or "start.end" or + * "start^end" + * + * @param location + * @return + * @throws ParseException + * if unable to parse the location (the exception message is the + * location specifier being parsed); we use ParseException in + * preference to the unchecked IllegalArgumentException + * @see http://www.insdc.org/files/feature_table.html#3.4 + */ + public static List parseLocation(String location) + throws ParseException + { + if (location.startsWith("join(")) + { + return parseJoin(location); + } + else if (location.startsWith("complement(")) + { + return parseComplement(location); + } + if (location.startsWith("order(")) + { + throw new ParseException(location, 0); + } + + /* + * try to parse m..n (or simply m) + */ + String[] range = location.split("\\.\\."); + if (range.length == 1 || range.length == 2) + { + try + { + int start = Integer.valueOf(range[0]); + int end = range.length == 1 ? start : Integer.valueOf(range[1]); + return Collections.singletonList(new int[] { start, end }); + } catch (NumberFormatException e) + { + /* + * could be a location like <1..888 or 1..>888 + */ + throw new ParseException(location, 0); + } + } + else + { + /* + * could be a location like 102.110 or 123^124 + */ + throw new ParseException(location, 0); + } + } + + /** + * Parses a complement(locationSpec) into a list of start-end ranges + * + * @param location + * @return + * @throws ParseException + */ + static List parseComplement(String location) throws ParseException + { + /* + * take what is inside complement() + */ + if (!location.endsWith(")")) + { + throw new ParseException(location, 0); + } + String toComplement = location.substring("complement(".length(), + location.length() - 1); + List ranges = parseLocation(toComplement); + + /* + * 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 + * @throws ParseException + */ + static List parseJoin(String location) throws ParseException + { + List ranges = new ArrayList(); + + /* + * take what is inside join() + */ + if (!location.endsWith(")")) + { + throw new ParseException(location, 0); + } + String joinedLocs = location.substring("join(".length(), + location.length() - 1); + String[] locations = joinedLocs.split(","); + for (String loc : locations) + { + List range = parseLocation(loc); + ranges.addAll(range); + } + return ranges; + } + +} diff --git a/src/jalview/util/ImageMaker.java b/src/jalview/util/ImageMaker.java index b7aa4ca..fcea21d 100755 --- a/src/jalview/util/ImageMaker.java +++ b/src/jalview/util/ImageMaker.java @@ -54,6 +54,12 @@ public class ImageMaker TYPE type; + private IProgressIndicator pIndicator; + + private long pSessionId; + + private boolean headless; + public enum TYPE { EPS("EPS", MessageManager.getString("label.eps_file"), getEPSChooser()), PNG( @@ -94,17 +100,14 @@ public class ImageMaker int height, File file, String fileTitle, IProgressIndicator pIndicator, long pSessionId, boolean headless) { + this.pIndicator = pIndicator; this.type = type; - + this.pSessionId = pSessionId; + this.headless = headless; if (file == null) { - if (pIndicator != null && !headless) - { - pIndicator.setProgressBar( - MessageManager.formatMessage( - "status.waiting_for_user_to_select_output_file", - type.name), pSessionId); - } + setProgressMessage(MessageManager.formatMessage( + "status.waiting_for_user_to_select_output_file", type.name)); JalviewFileChooser chooser; chooser = type.getChooser(); chooser.setFileView(new jalview.io.JalviewFileView()); @@ -120,12 +123,8 @@ public class ImageMaker } else { - if (pIndicator != null && !headless) - { - pIndicator.setProgressBar(MessageManager.formatMessage( - "status.cancelled_image_export_operation", type.name), - pSessionId); - } + setProgressMessage(MessageManager.formatMessage( + "status.cancelled_image_export_operation", type.name)); } } @@ -134,6 +133,9 @@ public class ImageMaker try { out = new FileOutputStream(file); + setProgressMessage(null); + setProgressMessage(MessageManager.formatMessage( + "status.exporting_alignment_as_x_file", type.getName())); if (type == TYPE.SVG) { setupSVG(width, height, fileTitle); @@ -146,19 +148,13 @@ public class ImageMaker { setupPNG(width, height); } - if (pIndicator != null && !headless) - { - pIndicator.setProgressBar( -MessageManager.formatMessage( - "status.export_complete", type.getName()), - pSessionId); - } + } catch (Exception ex) { System.out.println("Error creating " + type.getName() + " file."); - pIndicator.setProgressBar(MessageManager.formatMessage( - "info.error_creating_file", type.getName()), pSessionId); + setProgressMessage(MessageManager.formatMessage( + "info.error_creating_file", type.getName())); } } } @@ -214,6 +210,8 @@ MessageManager.formatMessage( if (renderStyle == null || eps.cancelled) { + setProgressMessage(MessageManager.formatMessage( + "status.cancelled_image_export_operation", "EPS")); return; } } @@ -233,6 +231,8 @@ MessageManager.formatMessage( pg.setAccurateTextMode(accurateText); graphics = pg; + setProgressMessage(MessageManager.formatMessage( + "status.export_complete", type.getName())); } catch (Exception ex) { } @@ -245,6 +245,8 @@ MessageManager.formatMessage( Graphics2D ig2 = (Graphics2D) graphics; ig2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + setProgressMessage(MessageManager.formatMessage( + "status.export_complete", type.getName())); } @@ -268,16 +270,20 @@ MessageManager.formatMessage( if (renderStyle == null || svgOption.cancelled) { + setProgressMessage(MessageManager.formatMessage( + "status.cancelled_image_export_operation", "SVG")); return; } } - if (renderStyle.equalsIgnoreCase("lineart")) + if (renderStyle.equalsIgnoreCase("Lineart")) { ig2.setRenderingHint(SVGHints.KEY_DRAW_STRING_TYPE, SVGHints.VALUE_DRAW_STRING_TYPE_VECTOR); } + setProgressMessage(MessageManager.formatMessage( + "status.export_complete", type.getName())); graphics = g2; } @@ -307,6 +313,14 @@ MessageManager.formatMessage( "Encapsulated Postscript"); } + private void setProgressMessage(String message) + { + if (pIndicator != null && !headless) + { + pIndicator.setProgressBar(message, pSessionId); + } + } + static JalviewFileChooser getSVGChooser() { if (Jalview.isHeadlessMode()) diff --git a/src/jalview/util/LinkedIdentityHashSet.java b/src/jalview/util/LinkedIdentityHashSet.java new file mode 100644 index 0000000..bce540f --- /dev/null +++ b/src/jalview/util/LinkedIdentityHashSet.java @@ -0,0 +1,143 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.util; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.LinkedHashMap; + +/** + * Order preserving Set based on System.identityHashCode() for an object, which + * also supports Object->index lookup. + * + * @author Jim Procter (2016) based on Evgeniy Dorofeev's response: via + * https://stackoverflow.com/questions/17276658/linkedidentityhashset + * + */ +public class LinkedIdentityHashSet extends AbstractSet +{ + LinkedHashMap set = new LinkedHashMap(); + + static class IdentityWrapper + { + Object obj; + + public int p; + + IdentityWrapper(Object obj, int p) + { + this.obj = obj; + this.p = p; + } + + @Override + public boolean equals(Object obj) + { + return this.obj == obj; + } + + @Override + public int hashCode() + { + return System.identityHashCode(obj); + } + } + + @Override + public boolean add(E e) + { + IdentityWrapper el = (new IdentityWrapper(e, set.size())); + // Map.putIfAbsent() from Java 8 + // return set.putIfAbsent(el, el) == null; + return putIfAbsent(el, el) == null; + } + + /** + * If the specified key is not already associated with a value (or is mapped + * to null) associates it with the given value and returns null, else returns + * the current value. + * + * Method added for Java 7 (can remove for Java 8) + * + * @param key + * @param value + * @return + * @see https + * ://docs.oracle.com/javase/8/docs/api/java/util/Map.html#putIfAbsent + * -K-V- + */ + private IdentityWrapper putIfAbsent(IdentityWrapper key, + IdentityWrapper value) + { + IdentityWrapper v = set.get(key); + if (v == null) + { + v = set.put(key, value); + } + return v; + } + + @Override + public Iterator iterator() + { + return new Iterator() + { + final Iterator se = set.keySet().iterator(); + + @Override + public boolean hasNext() + { + return se.hasNext(); + } + + @SuppressWarnings("unchecked") + @Override + public E next() + { + return (E) se.next().obj; + } + + @Override + public void remove() + { + // Java 8 default behaviour + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public int size() + { + return set.size(); + } + + /** + * Lookup the index for e in the set + * + * @param e + * @return position of e in the set when it was added. + */ + public int indexOf(E e) + { + return set.get(e).p; + } +} diff --git a/src/jalview/util/MapList.java b/src/jalview/util/MapList.java index e51442c..dc5bee8 100644 --- a/src/jalview/util/MapList.java +++ b/src/jalview/util/MapList.java @@ -88,8 +88,6 @@ public class MapList @Override public boolean equals(Object o) { - // TODO should also override hashCode to ensure equal objects have equal - // hashcodes if (o == null || !(o instanceof MapList)) { return false; @@ -112,6 +110,19 @@ public class MapList } /** + * Returns a hashcode made from the fromRatio, toRatio, and from/to ranges + */ + @Override + public int hashCode() + { + int hashCode = 31 * fromRatio; + hashCode = 31 * hashCode + toRatio; + hashCode = 31 * hashCode + fromShifts.toArray().hashCode(); + hashCode = 31 * hashCode + toShifts.toArray().hashCode(); + return hashCode; + } + + /** * Returns the 'from' ranges as {[start1, end1], [start2, end2], ...} * * @return @@ -215,7 +226,7 @@ public class MapList { /* * note lowest and highest values - bearing in mind the - * direction may be revesed + * direction may be reversed */ fromLowest = Math.min(fromLowest, Math.min(from[i], from[i + 1])); fromHighest = Math.max(fromHighest, Math.max(from[i], from[i + 1])); @@ -992,6 +1003,10 @@ public class MapList */ public void addMapList(MapList map) { + if (this.equals(map)) + { + return; + } this.fromLowest = Math.min(fromLowest, map.fromLowest); this.toLowest = Math.min(toLowest, map.toLowest); this.fromHighest = Math.max(fromHighest, map.fromHighest); @@ -1087,4 +1102,15 @@ public class MapList } return forwardStrand; } + + /** + * + * @return true if from, or to is a three to 1 mapping + */ + public boolean isTripletMap() + { + return (toRatio == 3 && fromRatio == 1) + || (fromRatio == 3 && toRatio == 1); + } + } diff --git a/src/jalview/util/MappingUtils.java b/src/jalview/util/MappingUtils.java index ae4e55d..da83338 100644 --- a/src/jalview/util/MappingUtils.java +++ b/src/jalview/util/MappingUtils.java @@ -754,6 +754,23 @@ public final class MappingUtils public static List findMappingsForSequence( SequenceI sequence, List mappings) { + return findMappingsForSequenceAndOthers(sequence, mappings, null); + } + + /** + * Returns a list of any mappings that are from or to the given (aligned or + * dataset) sequence, optionally limited to mappings involving one of a given + * list of sequences. + * + * @param sequence + * @param mappings + * @param filterList + * @return + */ + public static List findMappingsForSequenceAndOthers( + SequenceI sequence, List mappings, + List filterList) + { List result = new ArrayList(); if (sequence == null || mappings == null) { @@ -763,7 +780,31 @@ public final class MappingUtils { if (mapping.involvesSequence(sequence)) { - result.add(mapping); + if (filterList != null) + { + for (SequenceI otherseq : filterList) + { + SequenceI otherDataset = otherseq.getDatasetSequence(); + if (otherseq == sequence + || otherseq == sequence.getDatasetSequence() + || (otherDataset != null && (otherDataset == sequence || otherDataset == sequence + .getDatasetSequence()))) + { + // skip sequences in subset which directly relate to sequence + continue; + } + if (mapping.involvesSequence(otherseq)) + { + // selected a mapping contained in subselect alignment + result.add(mapping); + break; + } + } + } + else + { + result.add(mapping); + } } } return result; diff --git a/src/jalview/util/Platform.java b/src/jalview/util/Platform.java index b812feb..3fb384f 100644 --- a/src/jalview/util/Platform.java +++ b/src/jalview/util/Platform.java @@ -20,6 +20,9 @@ */ package jalview.util; +import java.awt.Toolkit; +import java.awt.event.MouseEvent; + /** * System platform information used by Applet and Application * @@ -74,4 +77,24 @@ public class Platform f.append(file.substring(lastp)); return f.toString(); } + + /** + * Answers true if the mouse event has Meta-down (Command key on Mac) or + * Ctrl-down (on other o/s). Note this answers _false_ if the Ctrl key is + * pressed instead of the Meta/Cmd key on Mac. To test for Ctrl-click on Mac, + * you can use e.isPopupTrigger(). + * + * @param e + * @return + */ + public static boolean isControlDown(MouseEvent e) + { + if (isAMac()) + { + return (Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() & e + .getModifiers()) != 0; + // could we use e.isMetaDown() here? + } + return e.isControlDown(); + } } diff --git a/src/jalview/util/StringUtils.java b/src/jalview/util/StringUtils.java index 6044655..ccc2012 100644 --- a/src/jalview/util/StringUtils.java +++ b/src/jalview/util/StringUtils.java @@ -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(); + } } diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index 1a42611..01668a5 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -845,14 +845,22 @@ public abstract class AlignmentViewport implements AlignViewportI, && !al.getCodonFrames().isEmpty()) { /* - * fudge - check first mapping is protein-to-nucleotide + * fudge - check first for protein-to-nucleotide mappings * (we don't want to do this for protein-to-protein) */ - AlignedCodonFrame mapping = al.getCodonFrames().iterator().next(); - // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame? - MapList[] mapLists = mapping.getdnaToProt(); - // mapLists can be empty if project load has not finished resolving seqs - if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3) + boolean doConsensus = false; + for (AlignedCodonFrame mapping : al.getCodonFrames()) + { + // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame? + MapList[] mapLists = mapping.getdnaToProt(); + // mapLists can be empty if project load has not finished resolving seqs + if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3) + { + doConsensus = true; + break; + } + } + if (doConsensus) { if (calculator .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null) @@ -1079,6 +1087,7 @@ public abstract class AlignmentViewport implements AlignViewportI, { updateHiddenColumns(); } + isColSelChanged(true); } /** @@ -1099,6 +1108,13 @@ public abstract class AlignmentViewport implements AlignViewportI, } @Override + public boolean hasSelectedColumns() + { + ColumnSelection columnSelection = getColumnSelection(); + return columnSelection != null && columnSelection.hasSelectedColumns(); + } + + @Override public boolean hasHiddenColumns() { return colSel != null && colSel.hasHiddenColumns(); @@ -1207,8 +1223,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) @@ -1309,7 +1324,7 @@ public abstract class AlignmentViewport implements AlignViewportI, colSel.hideSelectedColumns(); setSelectionGroup(null); - + isColSelChanged(true); } public void hideColumns(int start, int end) @@ -1322,17 +1337,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 @@ -1412,6 +1429,39 @@ public abstract class AlignmentViewport implements AlignViewportI, } /** + * Hides the specified sequence, or the sequences it represents + * + * @param sequence + * the sequence to hide, or keep as representative + * @param representGroup + * if true, hide the current selection group except for the + * representative sequence + */ + public void hideSequences(SequenceI sequence, boolean representGroup) + { + if (selectionGroup == null || selectionGroup.getSize() < 1) + { + hideSequence(new SequenceI[] { sequence }); + return; + } + + if (representGroup) + { + hideRepSequences(sequence, selectionGroup); + setSelectionGroup(null); + return; + } + + int gsize = selectionGroup.getSize(); + SequenceI[] hseqs = selectionGroup.getSequences().toArray( + new SequenceI[gsize]); + + hideSequence(hseqs); + setSelectionGroup(null); + sendSelection(); + } + + /** * Set visibility for any annotations for the given sequence. * * @param sequenceI @@ -1419,11 +1469,15 @@ public abstract class AlignmentViewport implements AlignViewportI, protected void setSequenceAnnotationsVisible(SequenceI sequenceI, boolean visible) { - for (AlignmentAnnotation ann : alignment.getAlignmentAnnotation()) + AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation(); + if (anns != null) { - if (ann.sequenceRef == sequenceI) + for (AlignmentAnnotation ann : anns) { - ann.visible = visible; + if (ann.sequenceRef == sequenceI) + { + ann.visible = visible; + } } } } @@ -1587,6 +1641,13 @@ public abstract class AlignmentViewport implements AlignViewportI, @Override public String[] getViewAsString(boolean selectedRegionOnly) { + return getViewAsString(selectedRegionOnly, true); + } + + @Override + public String[] getViewAsString(boolean selectedRegionOnly, + boolean exportHiddenSeqs) + { String[] selection = null; SequenceI[] seqs = null; int i, iSize; @@ -1600,13 +1661,13 @@ public abstract class AlignmentViewport implements AlignViewportI, } else { - if (hasHiddenRows()) + if (hasHiddenRows() && exportHiddenSeqs) { - iSize = alignment.getHiddenSequences().getFullAlignment() - .getHeight(); - seqs = alignment.getHiddenSequences().getFullAlignment() - .getSequencesArray(); - end = alignment.getHiddenSequences().getFullAlignment().getWidth(); + AlignmentI fullAlignment = alignment.getHiddenSequences() + .getFullAlignment(); + iSize = fullAlignment.getHeight(); + seqs = fullAlignment.getSequencesArray(); + end = fullAlignment.getWidth(); } else { @@ -1827,12 +1888,20 @@ public abstract class AlignmentViewport implements AlignViewportI, .getCodonFrames(); if (codonMappings != null && !codonMappings.isEmpty()) { - // fudge: check mappings are not protein-to-protein - // TODO: nicer - AlignedCodonFrame mapping = codonMappings.iterator().next(); - MapList[] mapLists = mapping.getdnaToProt(); - // mapLists can be empty if project load has not finished resolving seqs - if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3) + boolean doConsensus = false; + for (AlignedCodonFrame mapping : codonMappings) + { + // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame? + MapList[] mapLists = mapping.getdnaToProt(); + // mapLists can be empty if project load has not finished resolving + // seqs + if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3) + { + doConsensus = true; + break; + } + } + if (doConsensus) { complementConsensus = new AlignmentAnnotation("cDNA Consensus", "PID for cDNA", new Annotation[1], 0f, 100f, @@ -2655,6 +2724,7 @@ public abstract class AlignmentViewport implements AlignViewportI, * all gapped visible regions */ int lastSeq = alignment.getHeight() - 1; + List seqMappings = null; for (int seqNo = getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++) { sequence = getAlignment().getSequenceAt(seqNo); @@ -2666,15 +2736,16 @@ public abstract class AlignmentViewport implements AlignViewportI, { continue; } - List seqMappings = MappingUtils - .findMappingsForSequence(sequence, mappings); + seqMappings = MappingUtils + .findMappingsForSequenceAndOthers(sequence, mappings, + getCodingComplement().getAlignment().getSequences()); if (!seqMappings.isEmpty()) { break; } } - if (sequence == null) + if (sequence == null || seqMappings == null || seqMappings.isEmpty()) { /* * No ungapped mapped sequence in middle column - do nothing @@ -2682,7 +2753,7 @@ public abstract class AlignmentViewport implements AlignViewportI, return 0; } MappingUtils.addSearchResults(sr, sequence, - sequence.findPosition(middleColumn), mappings); + sequence.findPosition(middleColumn), seqMappings); return seqOffset; } @@ -2700,8 +2771,7 @@ public abstract class AlignmentViewport implements AlignViewportI, if (sg != null && (sgs = sg.getStartRes()) >= 0 && sg.getStartRes() <= (sge = sg.getEndRes()) - && (colSel == null || colSel.getSelected() == null || colSel - .getSelected().size() == 0)) + && !this.hasSelectedColumns()) { if (!wholewidth && alignment.getWidth() == (1 + sge - sgs)) { @@ -2718,4 +2788,33 @@ public abstract class AlignmentViewport implements AlignViewportI, } } } + + /** + * hold status of current selection group - defined on alignment or not. + */ + private boolean selectionIsDefinedGroup = false; + + @Override + public boolean isSelectionDefinedGroup() + { + if (selectionGroup == null) + { + return false; + } + if (isSelectionGroupChanged(true)) + { + selectionIsDefinedGroup = false; + List gps = alignment.getGroups(); + if (gps == null || gps.size() == 0) + { + selectionIsDefinedGroup = false; + } + else + { + selectionIsDefinedGroup = gps.contains(selectionGroup); + } + } + return selectionGroup.getContext() == alignment + || selectionIsDefinedGroup; + } } diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java index 52cfb50..9733081 100644 --- a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java +++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java @@ -31,7 +31,6 @@ import jalview.schemes.Colour; import jalview.schemes.FeatureColour; import jalview.schemes.UserColourScheme; import jalview.util.ColorUtils; -import jalview.viewmodel.AlignmentViewport; import java.awt.Color; import java.beans.PropertyChangeListener; @@ -66,7 +65,7 @@ public abstract class FeatureRendererModel implements protected PropertyChangeSupport changeSupport = new PropertyChangeSupport( this); - protected AlignmentViewport av; + protected AlignViewportI av; /* * map holds per feature type, {{min, max}, {min, max}} feature score @@ -664,7 +663,7 @@ public abstract class FeatureRendererModel implements * note visible feature ordering and colours before update */ List visibleFeatures = getDisplayedFeatureTypes(); - Map visibleColours = new HashMap( + Map visibleColours = new HashMap( getFeatureColours()); FeaturesDisplayedI av_featuresdisplayed = null; diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java index 908b5b4..296e9fd 100644 --- a/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java +++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererSettings.java @@ -32,8 +32,14 @@ public class FeatureRendererSettings implements Cloneable { String[] renderOrder; + /* + * map of {groupName, isDisplayed} + */ Map featureGroups; + /* + * map of {featureType, colourScheme} + */ Map featureColours; float transparency; diff --git a/src/jalview/workers/AlignCalcManager.java b/src/jalview/workers/AlignCalcManager.java index 1063706..addb372 100644 --- a/src/jalview/workers/AlignCalcManager.java +++ b/src/jalview/workers/AlignCalcManager.java @@ -34,23 +34,48 @@ import java.util.Set; public class AlignCalcManager implements AlignCalcManagerI { - private volatile List restartable = Collections - .synchronizedList(new ArrayList()); + /* + * list of registered workers + */ + private volatile List restartable; - private volatile List blackList = Collections - .synchronizedList(new ArrayList()); + /* + * types of worker _not_ to run (for example, because they have + * previously thrown errors) + */ + private volatile List> blackList; - /** + /* * global record of calculations in progress */ - private volatile Map inProgress = Collections - .synchronizedMap(new Hashtable()); + private volatile List inProgress; - /** + /* * record of calculations pending or in progress in the current context */ - private volatile Map> updating = Collections - .synchronizedMap(new Hashtable>()); + private volatile Map, List> updating; + + /* + * workers that have run to completion so are candidates for visual-only + * update of their results + */ + private HashSet canUpdate; + + /** + * Constructor + */ + public AlignCalcManager() + { + restartable = Collections + .synchronizedList(new ArrayList()); + blackList = Collections + .synchronizedList(new ArrayList>()); + inProgress = Collections + .synchronizedList(new ArrayList()); + updating = Collections + .synchronizedMap(new Hashtable, List>()); + canUpdate = new HashSet(); + } @Override public void notifyStart(AlignCalcWorkerI worker) @@ -72,15 +97,6 @@ public class AlignCalcManager implements AlignCalcManagerI } } - @Override - public boolean alreadyDoing(AlignCalcWorkerI worker) - { - synchronized (inProgress) - { - return inProgress.containsKey(worker.getClass()); - } - } - /* * (non-Javadoc) * @@ -108,52 +124,30 @@ public class AlignCalcManager implements AlignCalcManagerI } } - // TODO make into api method if needed ? - public int numberLive(AlignCalcWorkerI worker) - { - synchronized (updating) - { - List upd = updating.get(worker.getClass()); - if (upd == null) - { - return 0; - } - ; - return upd.size(); - } - } - @Override public boolean notifyWorking(AlignCalcWorkerI worker) { synchronized (inProgress) { - // TODO: decide if we should throw exceptions here if multiple workers - // start to work - if (inProgress.get(worker.getClass()) != null) + if (inProgress.contains(worker)) { - if (false) - { - System.err - .println("Warning: Multiple workers are running of type " - + worker.getClass()); - } - return false; + return false; // worker is already working, so ask caller to wait around + } + else + { + inProgress.add(worker); } - inProgress.put(worker.getClass(), worker); } return true; } - private final HashSet canUpdate = new HashSet(); - @Override public void workerComplete(AlignCalcWorkerI worker) { synchronized (inProgress) { - // System.err.println("Worker "+worker.getClass()+" marked as complete."); - inProgress.remove(worker.getClass()); + // System.err.println("Worker " + worker + " marked as complete."); + inProgress.remove(worker); List upd = updating.get(worker.getClass()); if (upd != null) { @@ -167,7 +161,7 @@ public class AlignCalcManager implements AlignCalcManagerI } @Override - public void workerCannotRun(AlignCalcWorkerI worker) + public void disableWorker(AlignCalcWorkerI worker) { synchronized (blackList) { @@ -175,22 +169,24 @@ public class AlignCalcManager implements AlignCalcManagerI } } - public boolean isBlackListed(Class workerType) + @Override + public boolean isDisabled(AlignCalcWorkerI worker) { synchronized (blackList) { - return blackList.contains(workerType); + return blackList.contains(worker.getClass()); } } @Override public void startWorker(AlignCalcWorkerI worker) { - // System.err.println("Starting "+worker.getClass()); - // new Exception("").printStackTrace(); - Thread tw = new Thread(worker); - tw.setName(worker.getClass().toString()); - tw.start(); + if (!isDisabled(worker)) + { + Thread tw = new Thread(worker); + tw.setName(worker.getClass().toString()); + tw.start(); + } } @Override @@ -199,7 +195,7 @@ public class AlignCalcManager implements AlignCalcManagerI synchronized (inProgress) {// System.err.println("isWorking : worker "+(worker!=null ? // worker.getClass():"null")+ " "+hashCode()); - return worker != null && inProgress.get(worker.getClass()) == worker; + return worker != null && inProgress.contains(worker); } } @@ -243,7 +239,7 @@ public class AlignCalcManager implements AlignCalcManagerI { synchronized (inProgress) { - for (AlignCalcWorkerI worker : inProgress.values()) + for (AlignCalcWorkerI worker : inProgress) { if (worker.involves(alignmentAnnotation)) { @@ -268,7 +264,8 @@ public class AlignCalcManager implements AlignCalcManagerI } @Override - public void updateAnnotationFor(Class workerClass) + public void updateAnnotationFor( + Class workerClass) { AlignCalcWorkerI[] workers; @@ -287,62 +284,35 @@ public class AlignCalcManager implements AlignCalcManagerI @Override public List getRegisteredWorkersOfClass( - Class workerClass) + Class workerClass) { List workingClass = new ArrayList(); - AlignCalcWorkerI[] workers; synchronized (canUpdate) { - workers = canUpdate.toArray(new AlignCalcWorkerI[canUpdate.size()]); - } - for (AlignCalcWorkerI worker : workers) - { - if (workerClass.equals(worker.getClass())) + for (AlignCalcWorkerI worker : canUpdate) { - workingClass.add(worker); + if (workerClass.equals(worker.getClass())) + { + workingClass.add(worker); + } } } return (workingClass.size() == 0) ? null : workingClass; } @Override - public boolean startRegisteredWorkersOfClass(Class workerClass) - { - List workers = getRegisteredWorkersOfClass(workerClass); - if (workers == null) - { - return false; - } - for (AlignCalcWorkerI worker : workers) - { - if (!isPending(worker)) - { - startWorker(worker); - } - else - { - System.err.println("Pending exists for " + workerClass); - } - } - return true; - } - - @Override - public void workerMayRun(AlignCalcWorkerI worker) + public void enableWorker(AlignCalcWorkerI worker) { synchronized (blackList) { - if (blackList.contains(worker.getClass())) - { - blackList.remove(worker.getClass()); - } + blackList.remove(worker.getClass()); } } @Override - public void removeRegisteredWorkersOfClass(Class typeToRemove) + public void removeRegisteredWorkersOfClass( + Class typeToRemove) { - List workers = getRegisteredWorkersOfClass(typeToRemove); List removable = new ArrayList(); Set toremovannot = new HashSet(); synchronized (restartable) @@ -382,4 +352,48 @@ public class AlignCalcManager implements AlignCalcManagerI * else { System.err.println("Pending exists for " + workerClass); } } */ } + + /** + * Deletes the worker that update the given annotation, provided it is marked + * as deletable. + */ + @Override + public void removeWorkerForAnnotation(AlignmentAnnotation ann) + { + /* + * first just find those to remove (to avoid + * ConcurrentModificationException) + */ + List toRemove = new ArrayList(); + for (AlignCalcWorkerI worker : restartable) + { + if (worker.involves(ann)) + { + if (worker.isDeletable()) + { + toRemove.add(worker); + } + } + } + + /* + * remove all references to deleted workers so any references + * they hold to annotation data can be garbage collected + */ + for (AlignCalcWorkerI worker : toRemove) + { + restartable.remove(worker); + blackList.remove(worker.getClass()); + inProgress.remove(worker); + canUpdate.remove(worker); + synchronized (updating) + { + List upd = updating.get(worker.getClass()); + if (upd != null) + { + upd.remove(worker); + } + } + } + } } diff --git a/src/jalview/workers/AlignCalcWorker.java b/src/jalview/workers/AlignCalcWorker.java index 48e3604..771c492 100644 --- a/src/jalview/workers/AlignCalcWorker.java +++ b/src/jalview/workers/AlignCalcWorker.java @@ -26,6 +26,7 @@ import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; +import jalview.datamodel.Annotation; import java.util.List; @@ -97,4 +98,39 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI // TODO: allow GUI to query workers associated with annotation to add items to // annotation label panel popup menu + @Override + public boolean isDeletable() + { + return false; + } + + /** + * Calculate min and max values of annotations and set as graphMin, graphMax + * on the AlignmentAnnotation. This is needed because otherwise, well, bad + * things happen. + * + * @param ann + * @param anns + */ + protected void setGraphMinMax(AlignmentAnnotation ann, Annotation[] anns) + { + // TODO feels like this belongs inside AlignmentAnnotation! + float max = Float.MIN_VALUE; + float min = Float.MAX_VALUE; + boolean set = false; + for (Annotation a : anns) { + if (a != null) { + set = true; + float val = a.value; + max = Math.max(max, val); + min = Math.min(min, val); + } + } + if (set) + { + ann.graphMin = min; + ann.graphMax = max; + } + } + } diff --git a/src/jalview/workers/AlignmentAnnotationFactory.java b/src/jalview/workers/AlignmentAnnotationFactory.java index 37f3ca5..2b7d9e1 100644 --- a/src/jalview/workers/AlignmentAnnotationFactory.java +++ b/src/jalview/workers/AlignmentAnnotationFactory.java @@ -1,9 +1,11 @@ package jalview.workers; +import jalview.api.AlignViewportI; +import jalview.api.AlignmentViewPanel; +import jalview.bin.Jalview; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; import jalview.gui.AlignFrame; -import jalview.gui.Desktop; import java.awt.Color; @@ -12,9 +14,9 @@ import java.awt.Color; * such as Groovy) to 'register and forget' an alignment annotation calculator.
          * Currently supports two flavours of calculator: *

            - *
          • a 'feature counter' which can count any desired property derivable from + *
          • a simple 'feature counter' which counts any desired score derivable from * residue value and any sequence features at each position of the alignment
          • - *
          • a 'general purpose' calculator which computes one more complete + *
          • a 'general purpose' calculator which computes one or more complete * AlignmentAnnotation objects
          • *
          */ @@ -28,9 +30,13 @@ public class AlignmentAnnotationFactory */ public static void newCalculator(FeatureCounterI counter) { - if (Desktop.getCurrentAlignFrame() != null) + // TODO need an interface for AlignFrame by which to access + // its AlignViewportI and AlignmentViewPanel + AlignmentViewPanel currentAlignFrame = Jalview.getCurrentAlignFrame().alignPanel; + if (currentAlignFrame != null) { - newCalculator(Desktop.getCurrentAlignFrame(), counter); + newCalculator(currentAlignFrame.getAlignViewport(), + currentAlignFrame, counter); } else { @@ -42,14 +48,15 @@ public class AlignmentAnnotationFactory /** * Constructs and registers a new alignment annotation worker * - * @param af - * the AlignFrame for which the annotation is to be calculated + * @param viewport + * @param panel * @param counter * provider of feature counts per alignment position */ - public static void newCalculator(AlignFrame af, FeatureCounterI counter) + public static void newCalculator(AlignViewportI viewport, + AlignmentViewPanel panel, FeatureCounterI counter) { - new ColumnCounterWorker(af, counter); + new ColumnCounterWorker(viewport, panel, counter); } /** @@ -60,9 +67,13 @@ public class AlignmentAnnotationFactory */ public static void newCalculator(AnnotationProviderI calculator) { - if (Desktop.getCurrentAlignFrame() != null) + // TODO need an interface for AlignFrame by which to access + // its AlignViewportI and AlignmentViewPanel + AlignFrame currentAlignFrame = Jalview.getCurrentAlignFrame() ; + if (currentAlignFrame != null) { - newCalculator(Desktop.getCurrentAlignFrame(), calculator); + newCalculator(currentAlignFrame.getViewport(), currentAlignFrame + .getAlignPanels().get(0), calculator); } else { @@ -74,15 +85,16 @@ public class AlignmentAnnotationFactory /** * Constructs and registers a new alignment annotation worker * - * @param af - * the AlignFrame for which the annotation is to be calculated + * @param viewport + * @param panel * @param calculator * provider of AlignmentAnnotation for the alignment */ - public static void newCalculator(AlignFrame af, + public static void newCalculator(AlignViewportI viewport, + AlignmentViewPanel panel, AnnotationProviderI calculator) { - new AnnotationWorker(af, calculator); + new AnnotationWorker(viewport, panel, calculator); } /** diff --git a/src/jalview/workers/AnnotationProviderI.java b/src/jalview/workers/AnnotationProviderI.java index 653ff04..bd24461 100644 --- a/src/jalview/workers/AnnotationProviderI.java +++ b/src/jalview/workers/AnnotationProviderI.java @@ -1,8 +1,8 @@ package jalview.workers; +import jalview.api.FeatureRenderer; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; -import jalview.gui.FeatureRenderer; import java.util.List; diff --git a/src/jalview/workers/AnnotationWorker.java b/src/jalview/workers/AnnotationWorker.java index fbf7531..4d81307 100644 --- a/src/jalview/workers/AnnotationWorker.java +++ b/src/jalview/workers/AnnotationWorker.java @@ -20,19 +20,19 @@ */ package jalview.workers; +import jalview.api.AlignViewportI; +import jalview.api.AlignmentViewPanel; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; -import jalview.gui.AlignFrame; -import jalview.gui.AlignmentPanel; -import jalview.gui.FeatureRenderer; +import jalview.renderer.seqfeatures.FeatureRenderer; import java.util.ArrayList; import java.util.List; /** * A class to create and update one or more alignment annotations, given a - * 'calculator'. - * + * 'calculator'. Intended to support a 'plug-in' annotation worker which + * implements the AnnotationProviderI interface. */ class AnnotationWorker extends AlignCalcWorker { @@ -47,9 +47,10 @@ class AnnotationWorker extends AlignCalcWorker * @param af * @param counter */ - public AnnotationWorker(AlignFrame af, AnnotationProviderI counter) + public AnnotationWorker(AlignViewportI viewport, + AlignmentViewPanel panel, AnnotationProviderI counter) { - super(af.getViewport(), af.alignPanel); + super(viewport, panel); ourAnnots = new ArrayList(); this.counter = counter; calcMan.registerWorker(this); @@ -78,20 +79,29 @@ class AnnotationWorker extends AlignCalcWorker return; } - removeAnnotations(); + // removeAnnotation(); AlignmentI alignment = alignViewport.getAlignment(); if (alignment != null) { try { List anns = counter.calculateAnnotation( - alignment, new FeatureRenderer((AlignmentPanel) ap)); + alignment, new FeatureRenderer(alignViewport)); for (AlignmentAnnotation ann : anns) { - ann.showAllColLabels = true; - ann.graph = AlignmentAnnotation.BAR_GRAPH; - ourAnnots.add(ann); - alignment.addAnnotation(ann); + AlignmentAnnotation theAnn = alignment.findOrCreateAnnotation( + ann.label, ann.description, false, null, null); + theAnn.showAllColLabels = true; + theAnn.graph = AlignmentAnnotation.BAR_GRAPH; + theAnn.scaleColLabel = true; + theAnn.annotations = ann.annotations; + setGraphMinMax(theAnn, theAnn.annotations); + theAnn.validateRangeAndDisplay(); + if (!ourAnnots.contains(theAnn)) + { + ourAnnots.add(theAnn); + } + // alignment.addAnnotation(ann); } } catch (IndexOutOfBoundsException x) { @@ -102,7 +112,7 @@ class AnnotationWorker extends AlignCalcWorker } catch (OutOfMemoryError error) { ap.raiseOOMWarning("calculating annotations", error); - calcMan.workerCannotRun(this); + calcMan.disableWorker(this); } finally { calcMan.workerComplete(this); @@ -113,24 +123,22 @@ class AnnotationWorker extends AlignCalcWorker ap.adjustAnnotationHeight(); ap.paintAlignment(true); } - } - /** - * Remove all our annotations before re-calculating them - */ - void removeAnnotations() + @Override + public void updateAnnotation() { - for (AlignmentAnnotation ann : ourAnnots) - { - alignViewport.getAlignment().deleteAnnotation(ann); - } - ourAnnots.clear(); + // do nothing } + /** + * Answers true to indicate that if this worker's annotation is deleted from + * the display, the worker should also be removed. This prevents it running + * and recreating the annotation when the alignment changes. + */ @Override - public void updateAnnotation() + public boolean isDeletable() { - // do nothing + return true; } } diff --git a/src/jalview/workers/ColumnCounterWorker.java b/src/jalview/workers/ColumnCounterWorker.java index 6f4a4f3..2f73cb5 100644 --- a/src/jalview/workers/ColumnCounterWorker.java +++ b/src/jalview/workers/ColumnCounterWorker.java @@ -20,14 +20,14 @@ */ package jalview.workers; +import jalview.api.AlignViewportI; +import jalview.api.AlignmentViewPanel; 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.renderer.seqfeatures.FeatureRenderer; import jalview.util.ColorUtils; import jalview.util.Comparison; @@ -53,9 +53,10 @@ class ColumnCounterWorker extends AlignCalcWorker * @param af * @param counter */ - public ColumnCounterWorker(AlignFrame af, FeatureCounterI counter) + public ColumnCounterWorker(AlignViewportI viewport, + AlignmentViewPanel panel, FeatureCounterI counter) { - super(af.getViewport(), af.alignPanel); + super(viewport, panel); ourAnnots = new ArrayList(); this.counter = counter; calcMan.registerWorker(this); @@ -88,7 +89,6 @@ class ColumnCounterWorker extends AlignCalcWorker return; } - removeAnnotation(); if (alignViewport.getAlignment() != null) { try @@ -103,7 +103,7 @@ class ColumnCounterWorker extends AlignCalcWorker } catch (OutOfMemoryError error) { ap.raiseOOMWarning("calculating feature counts", error); - calcMan.workerCannotRun(this); + calcMan.disableWorker(this); } finally { calcMan.workerComplete(this); @@ -123,7 +123,7 @@ class ColumnCounterWorker extends AlignCalcWorker */ void computeAnnotations() { - FeatureRenderer fr = new FeatureRenderer((AlignmentPanel) ap); + FeatureRenderer fr = new FeatureRenderer(alignViewport); // TODO use the commented out code once JAL-2075 is fixed // to get adequate performance on genomic length sequence AlignmentI alignment = alignViewport.getAlignment(); @@ -158,20 +158,29 @@ class ColumnCounterWorker extends AlignCalcWorker { Color color = ColorUtils.getGraduatedColour(count, 0, Color.cyan, max, Color.blue); - anns[i] = new Annotation(String.valueOf(count), - String.valueOf(count), '0', count, color); + String str = String.valueOf(count); + anns[i] = new Annotation(str, str, '0', count, color); } } /* - * construct the annotation, save it and add it to the displayed alignment + * construct or update the annotation */ - AlignmentAnnotation ann = new AlignmentAnnotation(counter.getName(), - counter.getDescription(), anns); + AlignmentAnnotation ann = alignViewport.getAlignment() + .findOrCreateAnnotation(counter.getName(), + counter.getDescription(), false, null, + null); + ann.description = counter.getDescription(); ann.showAllColLabels = true; + ann.scaleColLabel = true; ann.graph = AlignmentAnnotation.BAR_GRAPH; - ourAnnots.add(ann); - alignViewport.getAlignment().addAnnotation(ann); + ann.annotations = anns; + setGraphMinMax(ann, anns); + ann.validateRangeAndDisplay(); + if (!ourAnnots.contains(ann)) + { + ourAnnots.add(ann); + } } /** @@ -222,4 +231,15 @@ class ColumnCounterWorker extends AlignCalcWorker { // do nothing } + + /** + * Answers true to indicate that if this worker's annotation is deleted from + * the display, the worker should also be removed. This prevents it running + * and recreating the annotation when the alignment changes. + */ + @Override + public boolean isDeletable() + { + return true; + } } diff --git a/src/jalview/workers/ConsensusThread.java b/src/jalview/workers/ConsensusThread.java index 14e2a31..5f0ec84 100644 --- a/src/jalview/workers/ConsensusThread.java +++ b/src/jalview/workers/ConsensusThread.java @@ -96,7 +96,7 @@ public class ConsensusThread extends AlignCalcWorker } } catch (OutOfMemoryError error) { - calcMan.workerCannotRun(this); + calcMan.disableWorker(this); ap.raiseOOMWarning("calculating consensus", error); } finally { diff --git a/src/jalview/workers/ConservationThread.java b/src/jalview/workers/ConservationThread.java index 1075e4d..5c303fd 100644 --- a/src/jalview/workers/ConservationThread.java +++ b/src/jalview/workers/ConservationThread.java @@ -108,7 +108,7 @@ public class ConservationThread extends AlignCalcWorker } catch (OutOfMemoryError error) { ap.raiseOOMWarning("calculating conservation", error); - calcMan.workerCannotRun(this); + calcMan.disableWorker(this); // alignViewport.conservation = null; // this.alignViewport.quality = null; diff --git a/src/jalview/workers/StrucConsensusThread.java b/src/jalview/workers/StrucConsensusThread.java index 3483dac..5ed2885 100644 --- a/src/jalview/workers/StrucConsensusThread.java +++ b/src/jalview/workers/StrucConsensusThread.java @@ -94,12 +94,15 @@ public class StrucConsensusThread extends AlignCalcWorker .getAlignmentAnnotation(); AlignmentAnnotation rnaStruc = null; // select rna struct to use for calculation - for (int i = 0; i < aa.length; i++) + if (aa != null) { - if (aa[i].visible && aa[i].isRNA() && aa[i].isValidStruc()) + for (int i = 0; i < aa.length; i++) { - rnaStruc = aa[i]; - break; + if (aa[i].visible && aa[i].isRNA() && aa[i].isValidStruc()) + { + rnaStruc = aa[i]; + break; + } } } // check to see if its valid @@ -126,7 +129,7 @@ public class StrucConsensusThread extends AlignCalcWorker updateResultAnnotation(true); } catch (OutOfMemoryError error) { - calcMan.workerCannotRun(this); + calcMan.disableWorker(this); // consensus = null; // hconsensus = null; diff --git a/src/jalview/ws/AWSThread.java b/src/jalview/ws/AWSThread.java index b158448..2ef5256 100644 --- a/src/jalview/ws/AWSThread.java +++ b/src/jalview/ws/AWSThread.java @@ -31,9 +31,8 @@ import jalview.gui.WebserviceInfo; import jalview.util.MessageManager; import jalview.viewmodel.seqfeatures.FeatureRendererSettings; -import java.util.LinkedHashSet; +import java.util.ArrayList; import java.util.List; -import java.util.Set; public abstract class AWSThread extends Thread { @@ -61,7 +60,7 @@ public abstract class AWSThread extends Thread /** * dataset sequence relationships to be propagated onto new results */ - protected Set codonframe = null; + protected List codonframe = null; /** * are there jobs still running in this thread. @@ -384,7 +383,7 @@ public abstract class AWSThread extends Thread .getCodonFrames(); if (cf != null) { - codonframe = new LinkedHashSet(); + codonframe = new ArrayList(); codonframe.addAll(cf); } } diff --git a/src/jalview/ws/DBRefFetcher.java b/src/jalview/ws/DBRefFetcher.java index 40c88c1..ca403c5 100644 --- a/src/jalview/ws/DBRefFetcher.java +++ b/src/jalview/ws/DBRefFetcher.java @@ -141,7 +141,7 @@ public class DBRefFetcher implements Runnable String[] defdb = null, otherdb = sfetcher .getDbInstances(jalview.ws.dbsources.das.datamodel.DasSequenceSource.class); List selsources = new ArrayList(); - Vector dasselsrc = (featureSettings != null) ? featureSettings + Vector dasselsrc = (featureSettings != null) ? featureSettings .getSelectedSources() : new jalview.gui.DasSourceBrowser() .getSelectedSources(); Enumeration en = dasselsrc.elements(); @@ -176,6 +176,9 @@ public class DBRefFetcher implements Runnable srces.addAll(srcesfordb); } } + // append the PDB data source, since it is 'special', catering for both + // nucleotide and protein + srces.addAll(sfetcher.getSourceProxy(DBRefSource.PDB)); // append the selected sequence sources to the default dbs srces.addAll(selsources); @@ -190,6 +193,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 @@ -612,33 +625,43 @@ public class DBRefFetcher implements Runnable final int sequenceStart = sequence.getStart(); if (absStart == -1) { - // Is local sequence contained in dataset sequence? + // couldn't find local sequence in sequence from database, so check if + // the database sequence is a subsequence of local sequence absStart = nonGapped.indexOf(entrySeq); if (absStart == -1) - { // verification failed. + { + // verification failed. couldn't find any relationship between + // entrySeq and local sequence messages.append(sequence.getName() + " SEQUENCE NOT %100 MATCH \n"); continue; } + /* + * found match for the whole of the database sequence within the local + * sequence's reference frame. + */ transferred = true; sbuffer.append(sequence.getName() + " HAS " + absStart + " PREFIXED RESIDUES COMPARED TO " + dbSource + "\n"); - // - // + " - ANY SEQUENCE FEATURES" - // + " HAVE BEEN ADJUSTED ACCORDINGLY \n"); - // absStart = 0; - // create valid mapping between matching region of local sequence and - // the mapped sequence + + /* + * So create a mapping to the external entry from the matching region of + * the local sequence, and leave local start/end untouched. + */ mp = new Mapping(null, new int[] { sequenceStart + absStart, sequenceStart + absStart + entrySeq.length() - 1 }, new int[] { entry.getStart(), entry.getStart() + entrySeq.length() - 1 }, 1, 1); - updateRefFrame = false; // mapping is based on current start/end so - // don't modify start and end + updateRefFrame = false; } else { + /* + * found a match for the local sequence within sequence from + * the external database + */ transferred = true; + // update start and end of local sequence to place it in entry's // reference frame. // apply identity map map from whole of local sequence to matching @@ -650,10 +673,14 @@ public class DBRefFetcher implements Runnable // absStart+sequence.getStart()+entrySeq.length()-1}, // new int[] { entry.getStart(), entry.getEnd() }, 1, 1); // relocate local features for updated start + if (updateRefFrame) { if (sequence.getSequenceFeatures() != null) { + /* + * relocate existing sequence features by offset + */ SequenceFeature[] sf = sequence.getSequenceFeatures(); int start = sequenceStart; int end = sequence.getEnd(); @@ -676,7 +703,7 @@ public class DBRefFetcher implements Runnable System.out.println("Adding dbrefs to " + sequence.getName() + " from " + dbSource + " sequence : " + entry.getName()); sequence.transferAnnotation(entry, mp); - // unknownSequences.remove(sequence); + absStart += entry.getStart(); int absEnd = absStart + nonGapped.length() - 1; if (!trimDatasetSeqs) diff --git a/src/jalview/ws/DasSequenceFeatureFetcher.java b/src/jalview/ws/DasSequenceFeatureFetcher.java index d7ba24d..5f9b2d9 100644 --- a/src/jalview/ws/DasSequenceFeatureFetcher.java +++ b/src/jalview/ws/DasSequenceFeatureFetcher.java @@ -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 " diff --git a/src/jalview/ws/SequenceFetcher.java b/src/jalview/ws/SequenceFetcher.java index 65179a2..37946b1 100644 --- a/src/jalview/ws/SequenceFetcher.java +++ b/src/jalview/ws/SequenceFetcher.java @@ -27,10 +27,8 @@ import jalview.ws.dbsources.EmblSource; import jalview.ws.dbsources.Pdb; import jalview.ws.dbsources.PfamFull; import jalview.ws.dbsources.PfamSeed; -import jalview.ws.dbsources.RfamFull; import jalview.ws.dbsources.RfamSeed; import jalview.ws.dbsources.Uniprot; -import jalview.ws.dbsources.UniprotName; import jalview.ws.dbsources.das.api.jalviewSourceI; import jalview.ws.seqfetcher.ASequenceFetcher; import jalview.ws.seqfetcher.DbSourceProxy; @@ -62,11 +60,11 @@ public class SequenceFetcher extends ASequenceFetcher addDBRefSourceImpl(EmblSource.class); addDBRefSourceImpl(EmblCdsSource.class); addDBRefSourceImpl(Uniprot.class); - addDBRefSourceImpl(UniprotName.class); addDBRefSourceImpl(Pdb.class); addDBRefSourceImpl(PfamFull.class); addDBRefSourceImpl(PfamSeed.class); addDBRefSourceImpl(RfamSeed.class); + if (addDas) { registerDasSequenceSources(); diff --git a/src/jalview/ws/SequenceFetcherFactory.java b/src/jalview/ws/SequenceFetcherFactory.java new file mode 100644 index 0000000..2b8f364 --- /dev/null +++ b/src/jalview/ws/SequenceFetcherFactory.java @@ -0,0 +1,32 @@ +package jalview.ws; + +import jalview.ws.seqfetcher.ASequenceFetcher; + +public class SequenceFetcherFactory +{ + + private static SequenceFetcher instance; + + /** + * Returns a new SequenceFetcher object, or a mock object if one has been set + * + * @return + */ + public static ASequenceFetcher getSequenceFetcher() + { + return instance == null ? new SequenceFetcher() : instance; + } + + /** + * Set the instance object to use (intended for unit testing with mock + * objects). + * + * Be sure to reset to null in the tearDown method of any tests! + * + * @param sf + */ + public static void setSequenceFetcher(SequenceFetcher sf) + { + instance = sf; + } +} diff --git a/src/jalview/ws/dbsources/EmblXmlSource.java b/src/jalview/ws/dbsources/EmblXmlSource.java index 0085221..2049766 100644 --- a/src/jalview/ws/dbsources/EmblXmlSource.java +++ b/src/jalview/ws/dbsources/EmblXmlSource.java @@ -63,7 +63,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy try { reply = dbFetch.fetchDataAsFile( - emprefx.toLowerCase() + ":" + query.trim(), "emblxml", null, + emprefx.toLowerCase() + ":" + query.trim(), "display=xml", ".xml"); } catch (Exception e) { diff --git a/src/jalview/ws/dbsources/Pdb.java b/src/jalview/ws/dbsources/Pdb.java index 4a089f7..04c23a2 100644 --- a/src/jalview/ws/dbsources/Pdb.java +++ b/src/jalview/ws/dbsources/Pdb.java @@ -1,3 +1,4 @@ + /* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors @@ -26,9 +27,11 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; import jalview.datamodel.PDBEntry; +import jalview.datamodel.PDBEntry.Type; import jalview.datamodel.SequenceI; import jalview.io.FormatAdapter; import jalview.io.PDBFeatureSettings; +import jalview.structure.StructureImportSettings; import jalview.util.MessageManager; import jalview.ws.ebi.EBIFetchClient; @@ -53,8 +56,6 @@ public class Pdb extends EbiFileRetrievedProxy public static final String FEATURE_RES_NUM = "RESNUM"; - private static String currentDefaultFomart = DBRefSource.PDB; - /* * (non-Javadoc) * @@ -132,11 +133,12 @@ public class Pdb extends EbiFileRetrievedProxy stopQuery(); return null; } - String ext = getCurrentDefaultFomart().equalsIgnoreCase("mmcif") ? ".cif" - : ".xml"; + String ext = StructureImportSettings.getDefaultStructureFileFormat() + .equalsIgnoreCase(Type.MMCIF.toString()) ? ".cif" : ".xml"; EBIFetchClient ebi = new EBIFetchClient(); file = ebi.fetchDataAsFile("pdb:" + id, - getCurrentDefaultFomart().toLowerCase(), "raw", ext) + StructureImportSettings.getDefaultStructureFileFormat().toLowerCase(), + ext) .getAbsolutePath(); stopQuery(); if (file == null) @@ -148,7 +150,7 @@ public class Pdb extends EbiFileRetrievedProxy pdbAlignment = new FormatAdapter().readFile(file, jalview.io.AppletFormatAdapter.FILE, - getCurrentDefaultFomart()); + StructureImportSettings.getDefaultStructureFileFormat()); if (pdbAlignment != null) { List toremove = new ArrayList(); @@ -171,6 +173,8 @@ public class Pdb extends EbiFileRetrievedProxy || chid.trim().equals(chain.trim()) || (chain .trim().length() == 0 && chid.equals("_"))))) { + // FIXME seems to result in 'PDB|1QIP|1qip|A' - 1QIP is redundant. + // TODO: suggest simplify naming to 1qip|A as default name defined pdbcs.setName(jalview.datamodel.DBRefSource.PDB + "|" + id + "|" + pdbcs.getName()); // Might need to add more metadata to the PDBEntry object @@ -243,12 +247,12 @@ public class Pdb extends EbiFileRetrievedProxy } /** - * obtain human glyoxalase chain A sequence + * human glyoxalase */ @Override public String getTestQuery() { - return "1QIPA"; + return "1QIP"; } @Override @@ -263,15 +267,6 @@ public class Pdb extends EbiFileRetrievedProxy return 0; } - public static String getCurrentDefaultFomart() - { - return currentDefaultFomart; - } - - public static void setCurrentDefaultFomart(String currentDefaultFomart) - { - Pdb.currentDefaultFomart = currentDefaultFomart; - } /** * Returns a descriptor for suitable feature display settings with diff --git a/src/jalview/ws/dbsources/Uniprot.java b/src/jalview/ws/dbsources/Uniprot.java index 17f1842..81b4caf 100644 --- a/src/jalview/ws/dbsources/Uniprot.java +++ b/src/jalview/ws/dbsources/Uniprot.java @@ -165,7 +165,7 @@ public class Uniprot extends DbSourceProxyImpl // uniprotxml parameter required since december 2007 // uniprotkb dbname changed introduced december 2008 File file = ebi.fetchDataAsFile("uniprotkb:" + queries, "uniprotxml", - null, ".xml"); + ".xml"); Vector entries = getUniprotEntries(new FileReader(file)); if (entries != null) @@ -205,10 +205,10 @@ public class Uniprot extends DbSourceProxyImpl { DBRefEntry dbRef = new DBRefEntry(DBRefSource.UNIPROT, dbVersion, accessionId); + + // mark dbRef as a primary reference for this sequence dbRefs.add(dbRef); } - sequence.setSourceDBRef((dbRefs != null && dbRefs.size() > 0) ? dbRefs - .get(0) : null); Vector onlyPdbEntries = new Vector(); for (PDBEntry pdb : entry.getDbReference()) diff --git a/src/jalview/ws/ebi/EBIFetchClient.java b/src/jalview/ws/ebi/EBIFetchClient.java index 9f6bc65..1dff32f 100644 --- a/src/jalview/ws/ebi/EBIFetchClient.java +++ b/src/jalview/ws/ebi/EBIFetchClient.java @@ -42,9 +42,6 @@ import java.util.StringTokenizer; */ public class EBIFetchClient { - String format = "default"; - - String style = "raw"; /** * Creates a new EBIFetchClient object. @@ -93,14 +90,13 @@ public class EBIFetchClient * the query formatted as db:query1;query2;query3 * @param format * the format wanted - * @param s - * - unused parameter + * @param extension + * for the temporary file to hold response * @return the file holding the response * @throws OutOfMemoryError */ - public File fetchDataAsFile(String ids, String format, String s, - String ext) + public File fetchDataAsFile(String ids, String format, String ext) throws OutOfMemoryError { File outFile = null; @@ -108,7 +104,7 @@ public class EBIFetchClient { outFile = File.createTempFile("jalview", ext); outFile.deleteOnExit(); - fetchData(ids, format, s, outFile); + fetchData(ids, format, outFile); if (outFile.length() == 0) { outFile.delete(); @@ -121,92 +117,92 @@ public class EBIFetchClient } /** - * Single DB multiple record retrieval + * Fetches queries and either saves the response to a file or returns as + * string data * * @param ids - * db:query1;query2;query3 * @param format - * raw/xml - * @param s - * not used - remove? - * - * @return Raw string array result of query set + * @param outFile + * @return + * @throws OutOfMemoryError */ - public String[] fetchData(String ids, String format, String s) + String[] fetchData(String ids, String format, File outFile) throws OutOfMemoryError { - return fetchData(ids, format, s, null); + StringBuilder querystring = new StringBuilder(ids.length()); + String database = parseIds(ids, querystring); + if (database == null) + { + System.err.println("Invalid Query string : '" + ids + "'"); + System.err.println("Should be of form 'dbname:q1;q2;q3;q4'"); + return null; + } + + // note: outFile is currently always specified, so return value is null + String[] rslt = fetchBatch(querystring.toString(), database, format, outFile); + + return (rslt != null && rslt.length > 0 ? rslt : null); } - String[] fetchData(String ids, String f, String s, File outFile) - throws OutOfMemoryError + /** + * Parses ids formatted as dbname:q1;q2;q3, returns the dbname and adds + * queries as comma-separated items to the querystring. dbname must be + * specified for at least one queryId. Returns null if a mixture of different + * dbnames is found (ignoring case). + * + * @param ids + * @param queryString + * @return + */ + static String parseIds(String ids, StringBuilder queryString) { - // Need to split - // ids of the form uniprot:25KD_SARPE;ADHR_DROPS; - String[] rslts = new String[0]; + String database = null; StringTokenizer queries = new StringTokenizer(ids, ";"); - String db = null; - StringBuffer querystring = null; - int nq = 0; + boolean appending = queryString.length() > 0; while (queries.hasMoreTokens()) { String query = queries.nextToken(); - int p; - if ((p = query.indexOf(':')) > -1) + int p = query.indexOf(':'); + if (p > -1) { - db = query.substring(0, p); + String db = query.substring(0, p); + if (database != null && !db.equalsIgnoreCase(database)) + { + /* + * different databases mixed in together - invalid + */ + return null; + } + database = db; query = query.substring(p + 1); } - if (querystring == null) - { - querystring = new StringBuffer(query); - nq++; - } - else - { - querystring.append("," + query); - nq++; - } - } - if (db == null) - { - System.err.println("Invalid Query string : '" + ids - + "'\nShould be of form 'dbname:q1;q2;q3;q4'"); - return null; - } - String[] rslt = fetchBatch(querystring.toString(), db, f, s, outFile); - if (rslt != null) - { - String[] nrslts = new String[rslt.length + rslts.length]; - System.arraycopy(rslts, 0, nrslts, 0, rslts.length); - System.arraycopy(rslt, 0, nrslts, rslts.length, rslt.length); - rslts = nrslts; + queryString.append(appending ? "," : ""); + queryString.append(query); + appending = true; } - - return (rslts.length == 0 ? null : rslts); + return database; } - public String[] fetchBatch(String ids, String dbPath, String format, String s, + /** + * Fetches queries and either saves the response to a file or (if no file + * specified) returns as string data + * + * @param ids + * @param database + * @param format + * @param outFile + * @return + * @throws OutOfMemoryError + */ + String[] fetchBatch(String ids, String database, String format, File outFile) throws OutOfMemoryError { // long time = System.currentTimeMillis(); - /* - * JAL-1855 dbfetch from ena_sequence, ena_coding - */ - if (dbPath.equalsIgnoreCase(DBRefSource.EMBL)) - { - dbPath = "ena_sequence"; - } - else if (dbPath.equalsIgnoreCase(DBRefSource.EMBLCDS)) - { - dbPath = "ena_coding"; - } + String url = buildUrl(ids, database, format); try { - URL rcall = new URL("http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/" - + dbPath.toLowerCase() + "/" + ids.toLowerCase() - + (format != null ? "/" + format : "")); + URL rcall = new URL(url); InputStream is = new BufferedInputStream(rcall.openStream()); if (outFile != null) @@ -234,8 +230,7 @@ public class EBIFetchClient } } catch (OutOfMemoryError er) { - - System.out.println("OUT OF MEMORY DOWNLOADING QUERY FROM " + dbPath + System.out.println("OUT OF MEMORY DOWNLOADING QUERY FROM " + database + ":\n" + ids); throw er; } catch (Exception ex) @@ -246,7 +241,7 @@ public class EBIFetchClient return null; } System.err.println("Unexpected exception when retrieving from " - + dbPath + + database + "\nQuery was : '" + ids + "'"); ex.printStackTrace(System.err); return null; @@ -257,4 +252,30 @@ public class EBIFetchClient } return null; } + + /** + * Constructs the URL to fetch from + * + * @param ids + * @param database + * @param format + * @return + */ + static String buildUrl(String ids, String database, String format) + { + String url; + if (database.equalsIgnoreCase(DBRefSource.EMBL) + || database.equalsIgnoreCase(DBRefSource.EMBLCDS)) + { + url = "http://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase() + + (format != null ? "&" + format : ""); + } + else + { + url = "http://www.ebi.ac.uk/Tools/dbfetch/dbfetch/" + + database.toLowerCase() + "/" + ids.toLowerCase() + + (format != null ? "/" + format : ""); + } + return url; + } } diff --git a/src/jalview/ws/jws1/MsaWSClient.java b/src/jalview/ws/jws1/MsaWSClient.java index 95f5527..aad72b1 100644 --- a/src/jalview/ws/jws1/MsaWSClient.java +++ b/src/jalview/ws/jws1/MsaWSClient.java @@ -20,7 +20,7 @@ */ package jalview.ws.jws1; -import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.gui.AlignFrame; import jalview.gui.Desktop; @@ -34,7 +34,6 @@ import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; -import ext.vamsas.MuscleWS; import ext.vamsas.MuscleWSServiceLocator; import ext.vamsas.MuscleWSSoapBindingStub; import ext.vamsas.ServiceHandle; @@ -72,7 +71,7 @@ public class MsaWSClient extends WS1Client public MsaWSClient(ext.vamsas.ServiceHandle sh, String altitle, jalview.datamodel.AlignmentView msa, boolean submitGaps, - boolean preserveOrder, Alignment seqdataset, + boolean preserveOrder, AlignmentI seqdataset, AlignFrame _alignFrame) { super(); @@ -109,7 +108,7 @@ public class MsaWSClient extends WS1Client } private void startMsaWSClient(String altitle, AlignmentView msa, - boolean submitGaps, boolean preserveOrder, Alignment seqdataset) + boolean submitGaps, boolean preserveOrder, AlignmentI seqdataset) { if (!locateWebService()) { @@ -159,7 +158,7 @@ public class MsaWSClient extends WS1Client try { - this.server = (MuscleWS) loc.getMuscleWS(new java.net.URL(WsURL)); + this.server = loc.getMuscleWS(new java.net.URL(WsURL)); ((MuscleWSSoapBindingStub) this.server).setTimeout(60000); // One minute // timeout } catch (Exception ex) @@ -201,6 +200,7 @@ public class MsaWSClient extends WS1Client return (WebServiceName.indexOf("lustal") > -1); // cheat! } + @Override public void attachWSMenuEntry(JMenu msawsmenu, final ServiceHandle serviceHandle, final AlignFrame alignFrame) { @@ -209,6 +209,7 @@ public class MsaWSClient extends WS1Client method.setToolTipText(WsURL); method.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { AlignmentView msa = alignFrame.gatherSequencesForAlignment(); @@ -228,6 +229,7 @@ public class MsaWSClient extends WS1Client methodR.setToolTipText(WsURL); methodR.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { AlignmentView msa = alignFrame.gatherSequencesForAlignment(); diff --git a/src/jalview/ws/jws1/MsaWSThread.java b/src/jalview/ws/jws1/MsaWSThread.java index be21de7..3fd7c5a 100644 --- a/src/jalview/ws/jws1/MsaWSThread.java +++ b/src/jalview/ws/jws1/MsaWSThread.java @@ -23,6 +23,7 @@ package jalview.ws.jws1; import jalview.analysis.AlignSeq; import jalview.bin.Cache; import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentOrder; import jalview.datamodel.AlignmentView; import jalview.datamodel.ColumnSelection; @@ -147,6 +148,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI * * @return true if getAlignment will return a valid alignment result. */ + @Override public boolean hasResults() { if (subjobComplete && result != null && result.isFinished() @@ -273,6 +275,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI * * @return boolean true if job can be submitted. */ + @Override public boolean hasValidInput() { if (seqs.getSeqs() != null) @@ -285,7 +288,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI String alTitle; // name which will be used to form new alignment window. - Alignment dataset; // dataset to which the new alignment will be + AlignmentI dataset; // dataset to which the new alignment will be // associated. @@ -327,7 +330,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI MsaWSThread(ext.vamsas.MuscleWS server, String wsUrl, WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, String wsname, String title, AlignmentView _msa, boolean subgaps, - boolean presorder, Alignment seqset) + boolean presorder, AlignmentI seqset) { this(server, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder); OutputHeader = wsInfo.getProgressText(); @@ -359,11 +362,13 @@ class MsaWSThread extends JWS1Thread implements WSClientI } } + @Override public boolean isCancellable() { return true; } + @Override public void cancelJob() { if (!jobComplete && jobs != null) @@ -430,11 +435,13 @@ class MsaWSThread extends JWS1Thread implements WSClientI } } + @Override public void pollJob(AWsJob job) throws Exception { ((MsaWSJob) job).result = server.getResult(((MsaWSJob) job).getJobId()); } + @Override public void StartJob(AWsJob job) { if (!(job instanceof MsaWSJob)) @@ -521,6 +528,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI return msa; } + @Override public void parseResult() { int results = 0; // number of result sets received @@ -571,6 +579,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI wsInfo.showResultsNewFrame .addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent evt) { displayResults(true); @@ -579,6 +588,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI wsInfo.mergeResults .addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent evt) { displayResults(false); @@ -661,7 +671,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI while (j < l) { if (((AlignmentOrder) alorders.get(i)) - .equals(((AlignmentOrder) alorders.get(j)))) + .equals((alorders.get(j)))) { alorders.remove(j); l--; @@ -704,6 +714,7 @@ class MsaWSThread extends JWS1Thread implements WSClientI } } + @Override public boolean canMergeResults() { return false; diff --git a/src/jalview/ws/jws1/SeqSearchWSClient.java b/src/jalview/ws/jws1/SeqSearchWSClient.java index d731ced..2d83bf9 100644 --- a/src/jalview/ws/jws1/SeqSearchWSClient.java +++ b/src/jalview/ws/jws1/SeqSearchWSClient.java @@ -20,7 +20,7 @@ */ package jalview.ws.jws1; -import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.gui.AlignFrame; import jalview.gui.Desktop; @@ -39,7 +39,6 @@ import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; -import ext.vamsas.SeqSearchI; import ext.vamsas.SeqSearchServiceLocator; import ext.vamsas.SeqSearchServiceSoapBindingStub; import ext.vamsas.ServiceHandle; @@ -77,7 +76,7 @@ public class SeqSearchWSClient extends WS1Client public SeqSearchWSClient(ext.vamsas.ServiceHandle sh, String altitle, jalview.datamodel.AlignmentView msa, String db, - Alignment seqdataset, AlignFrame _alignFrame) + AlignmentI seqdataset, AlignFrame _alignFrame) { super(); alignFrame = _alignFrame; @@ -128,7 +127,7 @@ public class SeqSearchWSClient extends WS1Client } private void startSeqSearchClient(String altitle, AlignmentView msa, - String db, Alignment seqdataset) + String db, AlignmentI seqdataset) { if (!locateWebService()) { @@ -173,7 +172,7 @@ public class SeqSearchWSClient extends WS1Client try { - this.server = (SeqSearchI) loc.getSeqSearchService(new java.net.URL( + this.server = loc.getSeqSearchService(new java.net.URL( WsURL)); ((SeqSearchServiceSoapBindingStub) this.server).setTimeout(60000); // One // minute @@ -241,6 +240,7 @@ public class SeqSearchWSClient extends WS1Client return dbs; } + @Override public void attachWSMenuEntry(JMenu wsmenu, final ServiceHandle sh, final AlignFrame af) { @@ -281,6 +281,7 @@ public class SeqSearchWSClient extends WS1Client method.setToolTipText(sh.getEndpointURL()); method.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { // use same input gatherer as for secondary structure prediction @@ -305,6 +306,7 @@ public class SeqSearchWSClient extends WS1Client final String searchdb = dbs[db]; method.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { AlignmentView msa = af.gatherSeqOrMsaForSecStrPrediction(); diff --git a/src/jalview/ws/jws1/SeqSearchWSThread.java b/src/jalview/ws/jws1/SeqSearchWSThread.java index 66fddd1..70056a6 100644 --- a/src/jalview/ws/jws1/SeqSearchWSThread.java +++ b/src/jalview/ws/jws1/SeqSearchWSThread.java @@ -24,6 +24,7 @@ import jalview.analysis.AlignSeq; import jalview.api.FeatureColourI; import jalview.bin.Cache; import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.datamodel.SequenceI; import jalview.gui.AlignFrame; @@ -172,7 +173,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI * * @return null or { Alignment(+features and annotation), NewickFile)} */ - public Object[] getAlignment(Alignment dataset, + public Object[] getAlignment(AlignmentI dataset, Map featureColours) { @@ -303,7 +304,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI String alTitle; // name which will be used to form new alignment window. - Alignment dataset; // dataset to which the new alignment will be + AlignmentI dataset; // dataset to which the new alignment will be // associated. @@ -345,7 +346,7 @@ class SeqSearchWSThread extends JWS1Thread implements WSClientI SeqSearchWSThread(ext.vamsas.SeqSearchI server, String wsUrl, WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, String wsname, String title, AlignmentView _msa, String db, - Alignment seqset) + AlignmentI seqset) { this(server, wsUrl, wsinfo, alFrame, _msa, wsname, db); OutputHeader = wsInfo.getProgressText(); diff --git a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java index 897aa1e..b33df0c 100644 --- a/src/jalview/ws/jws2/AbstractJabaCalcWorker.java +++ b/src/jalview/ws/jws2/AbstractJabaCalcWorker.java @@ -331,23 +331,23 @@ public abstract class AbstractJabaCalcWorker extends AlignCalcWorker System.err.println("submission error with " + getServiceActionText() + " :"); x.printStackTrace(); - calcMan.workerCannotRun(this); + calcMan.disableWorker(this); } catch (ResultNotAvailableException x) { System.err.println("collection error:\nJob ID: " + rslt); x.printStackTrace(); - calcMan.workerCannotRun(this); + calcMan.disableWorker(this); } catch (OutOfMemoryError error) { - calcMan.workerCannotRun(this); + calcMan.disableWorker(this); // consensus = null; // hconsensus = null; ap.raiseOOMWarning(getServiceActionText(), error); } catch (Exception x) { - calcMan.workerCannotRun(this); + calcMan.disableWorker(this); // consensus = null; // hconsensus = null; diff --git a/src/jalview/ws/jws2/Jws2Client.java b/src/jalview/ws/jws2/Jws2Client.java index c0b701b..8855d96 100644 --- a/src/jalview/ws/jws2/Jws2Client.java +++ b/src/jalview/ws/jws2/Jws2Client.java @@ -256,11 +256,8 @@ public abstract class Jws2Client extends jalview.ws.WSClient }); wsmenu.add(aaConEnabled); final JMenuItem modifyParams = new JMenuItem(aaui.getAAeditSettings()); - modifyParams - .setToolTipText("

          " - + JvSwingUtils.wrapTooltip(false, - aaui.getAAeditSettingsTooltip() + "

          ") - + ""); + modifyParams.setToolTipText(JvSwingUtils.wrapTooltip(true, + aaui.getAAeditSettingsTooltip())); modifyParams.addActionListener(new ActionListener() { diff --git a/src/jalview/ws/jws2/MsaWSClient.java b/src/jalview/ws/jws2/MsaWSClient.java index 47130a3..758d941 100644 --- a/src/jalview/ws/jws2/MsaWSClient.java +++ b/src/jalview/ws/jws2/MsaWSClient.java @@ -20,7 +20,7 @@ */ package jalview.ws.jws2; -import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.gui.AlignFrame; import jalview.gui.Desktop; @@ -58,7 +58,7 @@ public class MsaWSClient extends Jws2Client public MsaWSClient(Jws2Instance sh, String altitle, jalview.datamodel.AlignmentView msa, boolean submitGaps, - boolean preserveOrder, Alignment seqdataset, + boolean preserveOrder, AlignmentI seqdataset, AlignFrame _alignFrame) { this(sh, null, null, false, altitle, msa, submitGaps, preserveOrder, @@ -68,7 +68,7 @@ public class MsaWSClient extends Jws2Client public MsaWSClient(Jws2Instance sh, WsParamSetI preset, String altitle, jalview.datamodel.AlignmentView msa, boolean submitGaps, - boolean preserveOrder, Alignment seqdataset, + boolean preserveOrder, AlignmentI seqdataset, AlignFrame _alignFrame) { this(sh, preset, null, false, altitle, msa, submitGaps, preserveOrder, @@ -95,7 +95,7 @@ public class MsaWSClient extends Jws2Client public MsaWSClient(Jws2Instance sh, WsParamSetI preset, List arguments, boolean editParams, String altitle, jalview.datamodel.AlignmentView msa, boolean submitGaps, - boolean preserveOrder, Alignment seqdataset, + boolean preserveOrder, AlignmentI seqdataset, AlignFrame _alignFrame) { super(_alignFrame, preset, arguments); @@ -138,7 +138,7 @@ public class MsaWSClient extends Jws2Client } private void startMsaWSClient(String altitle, AlignmentView msa, - boolean submitGaps, boolean preserveOrder, Alignment seqdataset) + boolean submitGaps, boolean preserveOrder, AlignmentI seqdataset) { // if (!locateWebService()) // { @@ -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, - "

          " + "" + (preset.isModifiable() ? MessageManager .getString("label.user_preset") : MessageManager .getString("label.service_preset")) - + "
          " + preset.getDescription() - + "

          ")); + + "
          " + + preset.getDescription()); + methodR.setToolTipText(tooltip); methodR.addActionListener(new ActionListener() { + @Override public void actionPerformed(ActionEvent e) { AlignmentView msa = alignFrame diff --git a/src/jalview/ws/jws2/MsaWSThread.java b/src/jalview/ws/jws2/MsaWSThread.java index e2f3a7c..e425624 100644 --- a/src/jalview/ws/jws2/MsaWSThread.java +++ b/src/jalview/ws/jws2/MsaWSThread.java @@ -176,6 +176,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI * * @return true if getAlignment will return a valid alignment result. */ + @Override public boolean hasResults() { if (subjobComplete @@ -316,6 +317,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI * * @return boolean true if job can be submitted. */ + @Override public boolean hasValidInput() { // TODO: get attributes for this MsaWS instance to check if it can do two @@ -436,7 +438,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI String alTitle; // name which will be used to form new alignment window. - Alignment dataset; // dataset to which the new alignment will be + AlignmentI dataset; // dataset to which the new alignment will be // associated. @@ -479,7 +481,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI String wsUrl, WebserviceInfo wsinfo, jalview.gui.AlignFrame alFrame, String wsname, String title, AlignmentView _msa, boolean subgaps, boolean presorder, - Alignment seqset) + AlignmentI seqset) { this(server2, wsUrl, wsinfo, alFrame, _msa, wsname, subgaps, presorder); OutputHeader = wsInfo.getProgressText(); @@ -530,11 +532,13 @@ class MsaWSThread extends AWS2Thread implements WSClientI return validInput; } + @Override public boolean isCancellable() { return true; } + @Override public void cancelJob() { if (!jobComplete && jobs != null) @@ -605,6 +609,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI } } + @Override public void pollJob(AWsJob job) throws Exception { // TODO: investigate if we still need to cast here in J1.6 @@ -650,6 +655,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI return changed; } + @Override public void StartJob(AWsJob job) { Exception lex = null; @@ -775,6 +781,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI } } + @Override public void parseResult() { long progbar = System.currentTimeMillis(); @@ -889,6 +896,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI wsInfo.showResultsNewFrame .addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent evt) { displayResults(true); @@ -897,6 +905,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI wsInfo.mergeResults .addActionListener(new java.awt.event.ActionListener() { + @Override public void actionPerformed(java.awt.event.ActionEvent evt) { displayResults(false); @@ -1023,6 +1032,10 @@ class MsaWSThread extends AWS2Thread implements WSClientI // becomes null if the alignment window was closed before the alignment // job finished. AlignmentI copyComplement = new Alignment(complement); + // todo should this be done by copy constructor? + copyComplement.setGapCharacter(complement.getGapCharacter()); + // share the same dataset (and the mappings it holds) + copyComplement.setDataset(complement.getDataset()); copyComplement.alignAs(al); if (copyComplement.getHeight() > 0) { @@ -1101,6 +1114,7 @@ class MsaWSThread extends AWS2Thread implements WSClientI } } + @Override public boolean canMergeResults() { return false; diff --git a/src/jalview/ws/jws2/SequenceAnnotationWSClient.java b/src/jalview/ws/jws2/SequenceAnnotationWSClient.java index 187540c..2af31bb 100644 --- a/src/jalview/ws/jws2/SequenceAnnotationWSClient.java +++ b/src/jalview/ws/jws2/SequenceAnnotationWSClient.java @@ -122,7 +122,7 @@ public class SequenceAnnotationWSClient extends Jws2Client } // reinstate worker if it was blacklisted (might have happened due to // invalid parameters) - alignFrame.getViewport().getCalcManager().workerMayRun(worker); + alignFrame.getViewport().getCalcManager().enableWorker(worker); worker.updateParameters(this.preset, paramset); } } diff --git a/src/jalview/ws/seqfetcher/ASequenceFetcher.java b/src/jalview/ws/seqfetcher/ASequenceFetcher.java index 2392476..33a917e 100644 --- a/src/jalview/ws/seqfetcher/ASequenceFetcher.java +++ b/src/jalview/ws/seqfetcher/ASequenceFetcher.java @@ -55,7 +55,7 @@ public class ASequenceFetcher /** * Constructor */ - public ASequenceFetcher() + protected ASequenceFetcher() { super(); @@ -125,20 +125,20 @@ public class ASequenceFetcher * if true, only fetch from nucleotide data sources, else peptide * @return */ - public SequenceI[] getSequences(DBRefEntry[] refs, boolean dna) + public SequenceI[] getSequences(List refs, boolean dna) { Vector rseqs = new Vector(); Hashtable> queries = new Hashtable>(); - for (int r = 0; r < refs.length; r++) + for (DBRefEntry ref : refs) { - if (!queries.containsKey(refs[r].getSource())) + if (!queries.containsKey(ref.getSource())) { - queries.put(refs[r].getSource(), new ArrayList()); + queries.put(ref.getSource(), new ArrayList()); } - List qset = queries.get(refs[r].getSource()); - if (!qset.contains(refs[r].getAccessionId())) + List qset = queries.get(ref.getSource()); + if (!qset.contains(ref.getAccessionId())) { - qset.add(refs[r].getAccessionId()); + qset.add(ref.getAccessionId()); } } Enumeration e = queries.keys(); @@ -205,15 +205,12 @@ public class ASequenceFetcher for (int is = 0; is < seqs.length; is++) { rseqs.addElement(seqs[is]); - DBRefEntry[] frefs = DBRefUtils.searchRefs(seqs[is] + List frefs = DBRefUtils.searchRefs(seqs[is] .getDBRefs(), new DBRefEntry(db, null, null)); - if (frefs != null) + for (DBRefEntry dbr : frefs) { - for (DBRefEntry dbr : frefs) - { - queriesFound.add(dbr.getAccessionId()); - queriesMade.remove(dbr.getAccessionId()); - } + queriesFound.add(dbr.getAccessionId()); + queriesMade.remove(dbr.getAccessionId()); } seqs[is] = null; } diff --git a/src/jalview/ws/sifts/MappingOutputPojo.java b/src/jalview/ws/sifts/MappingOutputPojo.java index c06e51f..aa65f49 100644 --- a/src/jalview/ws/sifts/MappingOutputPojo.java +++ b/src/jalview/ws/sifts/MappingOutputPojo.java @@ -20,8 +20,6 @@ public class MappingOutputPojo private String type; - private int wrapHeight; - private static final int MAX_ID_LENGTH = 30; public String getSeqName() @@ -116,14 +114,5 @@ public class MappingOutputPojo this.type = type; } - public int getWrapHeight() - { - return wrapHeight; - } - - public void setWrapHeight(int wrapHeight) - { - this.wrapHeight = wrapHeight; - } } diff --git a/src/jalview/ws/sifts/SiftsClient.java b/src/jalview/ws/sifts/SiftsClient.java index e04bbb7..f20954e 100644 --- a/src/jalview/ws/sifts/SiftsClient.java +++ b/src/jalview/ws/sifts/SiftsClient.java @@ -29,6 +29,7 @@ import jalview.datamodel.SequenceI; import jalview.io.StructureFile; import jalview.schemes.ResidueProperties; import jalview.structure.StructureMapping; +import jalview.util.DBRefUtils; import jalview.util.Format; import jalview.xml.binding.sifts.Entry; import jalview.xml.binding.sifts.Entry.Entity; @@ -37,11 +38,9 @@ import jalview.xml.binding.sifts.Entry.Entity.Segment.ListMapRegion.MapRegion; import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue; import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.CrossRefDb; import jalview.xml.binding.sifts.Entry.Entity.Segment.ListResidue.Residue.ResidueDetail; -import jalview.xml.binding.sifts.Entry.ListDB.Db; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -59,20 +58,18 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.TreeMap; import java.util.zip.GZIPInputStream; import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; -import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import MCview.Atom; import MCview.PDBChain; -import MCview.PDBfile; public class SiftsClient implements SiftsClientI { @@ -84,8 +81,6 @@ public class SiftsClient implements SiftsClientI private String structId; - private String segStartEnd; - private CoordinateSys seqCoordSys = CoordinateSys.UNIPROT; private static final int BUFFER_SIZE = 4096; @@ -96,11 +91,9 @@ public class SiftsClient implements SiftsClientI private static final int PDB_ATOM_POS = 1; - private static final String NOT_FOUND = "Not_Found"; - private static final String NOT_OBSERVED = "Not_Observed"; - private static final String SIFTS_FTP_BASE_URL = "ftp://ftp.ebi.ac.uk/pub/databases/msd/sifts/xml/"; + private static final String SIFTS_FTP_BASE_URL = "http://ftp.ebi.ac.uk/pub/databases/msd/sifts/xml/"; private final static String NEWLINE = System.lineSeparator(); @@ -108,7 +101,7 @@ public class SiftsClient implements SiftsClientI private HashSet curDBRefAccessionIdsString; - public enum CoordinateSys + private enum CoordinateSys { UNIPROT("UniProt"), PDB("PDBresnum"), PDBe("PDBe"); private String name; @@ -124,7 +117,7 @@ public class SiftsClient implements SiftsClientI } }; - public enum ResidueDetailType + private enum ResidueDetailType { NAME_SEC_STRUCTURE("nameSecondaryStructure"), CODE_SEC_STRUCTURE( "codeSecondaryStructure"), ANNOTATION("Annotation"); @@ -156,21 +149,6 @@ public class SiftsClient implements SiftsClientI siftsEntry = parseSIFTs(siftsFile); } - /** - * Construct an instance of SiftsClient using the supplied SIFTs file. Note: - * The SIFTs file should correspond to the PDB Id in PDBfile instance - * - * @param pdbId - * @param siftsFile - * @throws SiftsException - * @throws Exception - */ - public SiftsClient(PDBfile pdb, File siftsFile) throws SiftsException - { - this.pdb = pdb; - this.pdbId = pdb.getId(); - siftsEntry = parseSIFTs(siftsFile); - } /** * Parse the given SIFTs File and return a JAXB POJO of parsed data @@ -192,23 +170,7 @@ public class SiftsClient implements SiftsClientI .createXMLStreamReader(gzis); Unmarshaller um = jc.createUnmarshaller(); return (Entry) um.unmarshal(streamReader); - } catch (JAXBException e) - { - e.printStackTrace(); - throw new SiftsException(e.getMessage()); - } catch (FileNotFoundException e) - { - e.printStackTrace(); - throw new SiftsException(e.getMessage()); - } catch (XMLStreamException e) - { - e.printStackTrace(); - throw new SiftsException(e.getMessage()); - } catch (FactoryConfigurationError e) - { - e.printStackTrace(); - throw new SiftsException(e.getMessage()); - } catch (IOException e) + } catch (Exception e) { e.printStackTrace(); throw new SiftsException(e.getMessage()); @@ -225,8 +187,9 @@ public class SiftsClient implements SiftsClientI */ public static File getSiftsFile(String pdbId) throws SiftsException { - File siftsFile = new File(SiftsSettings.getSiftDownloadDirectory() - + pdbId.toLowerCase() + ".xml.gz"); + String siftsFileName = SiftsSettings.getSiftDownloadDirectory() + + pdbId.toLowerCase() + ".xml.gz"; + File siftsFile = new File(siftsFileName); if (siftsFile.exists()) { // The line below is required for unit testing... don't comment it out!!! @@ -235,12 +198,28 @@ public class SiftsClient implements SiftsClientI if (isFileOlderThanThreshold(siftsFile, SiftsSettings.getCacheThresholdInDays())) { - // System.out.println("Downloaded file is out of date, hence re-downloading..."); - siftsFile = downloadSiftsFile(pdbId.toLowerCase()); + File oldSiftsFile = new File(siftsFileName + "_old"); + siftsFile.renameTo(oldSiftsFile); + try + { + siftsFile = downloadSiftsFile(pdbId.toLowerCase()); + oldSiftsFile.delete(); + return siftsFile; + } catch (IOException e) + { + e.printStackTrace(); + oldSiftsFile.renameTo(siftsFile); + return new File(siftsFileName); + } } - return siftsFile; } - siftsFile = downloadSiftsFile(pdbId.toLowerCase()); + try + { + siftsFile = downloadSiftsFile(pdbId.toLowerCase()); + } catch (IOException e) + { + throw new SiftsException(e.getMessage()); + } return siftsFile; } @@ -278,8 +257,10 @@ public class SiftsClient implements SiftsClientI * @param pdbId * @return downloaded SIFTs XML file * @throws SiftsException + * @throws IOException */ - public static File downloadSiftsFile(String pdbId) throws SiftsException + public static File downloadSiftsFile(String pdbId) throws SiftsException, + IOException { if (pdbId.contains(".cif")) { @@ -295,8 +276,6 @@ public class SiftsClient implements SiftsClientI { siftsDownloadDir.mkdirs(); } - try - { // System.out.println(">> Download ftp url : " + siftsFileFTPURL); URL url = new URL(siftsFileFTPURL); URLConnection conn = url.openConnection(); @@ -312,10 +291,6 @@ public class SiftsClient implements SiftsClientI outputStream.close(); inputStream.close(); // System.out.println(">>> File downloaded : " + downloadedSiftsFile); - } catch (IOException ex) - { - throw new SiftsException(ex.getMessage()); - } return new File(downloadedSiftsFile); } @@ -349,39 +324,29 @@ public class SiftsClient implements SiftsClientI public DBRefEntryI getValidSourceDBRef(SequenceI seq) throws SiftsException { - DBRefEntryI sourceDBRef = null; - sourceDBRef = seq.getSourceDBRef(); - if (sourceDBRef != null && isValidDBRefEntry(sourceDBRef)) + List dbRefs = seq.getPrimaryDBRefs(); + if (dbRefs == null || dbRefs.size() < 1) { - return sourceDBRef; + throw new SiftsException( + "Source DBRef could not be determined. DBRefs might not have been retrieved."); } - else + + for (DBRefEntry dbRef : dbRefs) { - DBRefEntry[] dbRefs = seq.getDBRefs(); - if (dbRefs == null || dbRefs.length < 1) + if (dbRef == null || dbRef.getAccessionId() == null + || dbRef.getSource() == null) { - throw new SiftsException("Could not get source DB Ref"); + continue; } - - for (DBRefEntryI dbRef : dbRefs) + String canonicalSource = DBRefUtils.getCanonicalName(dbRef + .getSource()); + if (isValidDBRefEntry(dbRef) + && (canonicalSource.equalsIgnoreCase(DBRefSource.UNIPROT) || canonicalSource + .equalsIgnoreCase(DBRefSource.PDB))) { - if (dbRef == null || dbRef.getAccessionId() == null - || dbRef.getSource() == null) - { - continue; - } - if (isFoundInSiftsEntry(dbRef.getAccessionId()) - && (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT) || dbRef - .getSource().equalsIgnoreCase(DBRefSource.PDB))) - { - return dbRef; - } + return dbRef; } } - if (sourceDBRef != null && isValidDBRefEntry(sourceDBRef)) - { - return sourceDBRef; - } throw new SiftsException("Could not get source DB Ref"); } @@ -393,7 +358,7 @@ public class SiftsClient implements SiftsClientI * - DBRefEntry to validate * @return true validation is successful otherwise false is returned. */ - private boolean isValidDBRefEntry(DBRefEntryI entry) + boolean isValidDBRefEntry(DBRefEntryI entry) { return entry != null && entry.getAccessionId() != null && isFoundInSiftsEntry(entry.getAccessionId()); @@ -413,7 +378,8 @@ public class SiftsClient implements SiftsClientI .getMapRegion(); for (MapRegion mapRegion : mapRegions) { - accessions.add(mapRegion.getDb().getDbAccessionId()); + accessions + .add(mapRegion.getDb().getDbAccessionId().toLowerCase()); } } } @@ -455,7 +421,7 @@ public class SiftsClient implements SiftsClientI public HashMap getGreedyMapping(String entityId, SequenceI seq, java.io.PrintStream os) throws SiftsException { - ArrayList omitNonObserved = new ArrayList(); + List omitNonObserved = new ArrayList(); int nonObservedShiftIndex = 0; // System.out.println("Generating mappings for : " + entityId); Entity entity = null; @@ -463,13 +429,10 @@ public class SiftsClient implements SiftsClientI String originalSeq = AlignSeq.extractGaps( jalview.util.Comparison.GapChars, seq.getSequenceAsString()); HashMap mapping = new HashMap(); - DBRefEntryI sourceDBRef = seq.getSourceDBRef(); - if (sourceDBRef == null) - { - sourceDBRef = getValidSourceDBRef(seq); - // TODO ensure sequence start/end is in the same coordinate system and - // consistent with the choosen sourceDBRef - } + DBRefEntryI sourceDBRef; + sourceDBRef = getValidSourceDBRef(seq); + // TODO ensure sequence start/end is in the same coordinate system and + // consistent with the choosen sourceDBRef // set sequence coordinate system - default value is UniProt if (sourceDBRef.getSource().equalsIgnoreCase(DBRefSource.PDB)) @@ -489,10 +452,91 @@ public class SiftsClient implements SiftsClientI TreeMap resNumMap = new TreeMap(); List segments = entity.getSegment(); + SegmentHelperPojo shp = new SegmentHelperPojo(seq, mapping, resNumMap, + omitNonObserved, nonObservedShiftIndex); + processSegments(segments, shp); + try + { + populateAtomPositions(entityId, mapping); + } catch (Exception e) + { + e.printStackTrace(); + } + if (seqCoordSys == CoordinateSys.UNIPROT) + { + padWithGaps(resNumMap, omitNonObserved); + } + int seqStart = UNASSIGNED; + int seqEnd = UNASSIGNED; + int pdbStart = UNASSIGNED; + int pdbEnd = UNASSIGNED; + + Integer[] keys = mapping.keySet().toArray(new Integer[0]); + Arrays.sort(keys); + if (keys.length < 1) + { + throw new SiftsException(">>> Empty SIFTS mapping generated!!"); + } + seqStart = keys[0]; + seqEnd = keys[keys.length - 1]; + + String matchedSeq = originalSeq; + if (seqStart != UNASSIGNED) + { + pdbStart = mapping.get(seqStart)[PDB_RES_POS]; + pdbEnd = mapping.get(seqEnd)[PDB_RES_POS]; + int orignalSeqStart = seq.getStart(); + if (orignalSeqStart >= 1) + { + int subSeqStart = (seqStart >= orignalSeqStart) ? seqStart + - orignalSeqStart : 0; + int subSeqEnd = seqEnd - (orignalSeqStart - 1); + subSeqEnd = originalSeq.length() < subSeqEnd ? originalSeq.length() + : subSeqEnd; + matchedSeq = originalSeq.substring(subSeqStart, subSeqEnd); + } + else + { + matchedSeq = originalSeq.substring(1, originalSeq.length()); + } + } + + StringBuilder targetStrucSeqs = new StringBuilder(); + for (String res : resNumMap.values()) + { + targetStrucSeqs.append(res); + } + + if (os != null) + { + MappingOutputPojo mop = new MappingOutputPojo(); + mop.setSeqStart(pdbStart); + mop.setSeqEnd(pdbEnd); + mop.setSeqName(seq.getName()); + mop.setSeqResidue(matchedSeq); + + mop.setStrStart(seqStart); + mop.setStrEnd(seqEnd); + mop.setStrName(structId); + mop.setStrResidue(targetStrucSeqs.toString()); + + mop.setType("pep"); + os.print(getMappingOutput(mop).toString()); + os.println(); + } + return mapping; + } + + void processSegments(List segments, SegmentHelperPojo shp) + { + SequenceI seq = shp.getSeq(); + HashMap mapping = shp.getMapping(); + TreeMap resNumMap = shp.getResNumMap(); + List omitNonObserved = shp.getOmitNonObserved(); + int nonObservedShiftIndex = shp.getNonObservedShiftIndex(); for (Segment segment : segments) { - segStartEnd = segment.getStart() + " - " + segment.getEnd(); - // System.out.println("Mapping segments : " + segment.getSegId() + "\\" + // System.out.println("Mapping segments : " + segment.getSegId() + "\\"s // + segStartEnd); List residues = segment.getListResidue().getResidue(); for (Residue residue : residues) @@ -566,77 +610,7 @@ public class SiftsClient implements SiftsClientI } } } - try - { - populateAtomPositions(entityId, mapping); - } catch (Exception e) - { - e.printStackTrace(); - } - if (seqCoordSys == CoordinateSys.UNIPROT) - { - padWithGaps(resNumMap, omitNonObserved); - } - int seqStart = UNASSIGNED; - int seqEnd = UNASSIGNED; - int pdbStart = UNASSIGNED; - int pdbEnd = UNASSIGNED; - - Integer[] keys = mapping.keySet().toArray(new Integer[0]); - Arrays.sort(keys); - if (keys.length < 1) - { - throw new SiftsException(">>> Empty SIFTS mapping generated!!"); - } - seqStart = keys[0]; - seqEnd = keys[keys.length - 1]; - - String matchedSeq = originalSeq; - if (seqStart != UNASSIGNED) - { - pdbStart = mapping.get(seqStart)[PDB_RES_POS]; - pdbEnd = mapping.get(seqEnd)[PDB_RES_POS]; - int orignalSeqStart = seq.getStart(); - if (orignalSeqStart >= 1) - { - int subSeqStart = (seqStart >= orignalSeqStart) ? seqStart - - orignalSeqStart : 0; - int subSeqEnd = seqEnd - (orignalSeqStart - 1); - subSeqEnd = originalSeq.length() < subSeqEnd ? originalSeq.length() - : subSeqEnd; - matchedSeq = originalSeq.substring(subSeqStart, subSeqEnd); - } - else - { - matchedSeq = originalSeq.substring(1, originalSeq.length()); - } - } - - StringBuilder targetStrucSeqs = new StringBuilder(); - for (String res : resNumMap.values()) - { - targetStrucSeqs.append(res); - } - - if (os != null) - { - MappingOutputPojo mop = new MappingOutputPojo(); - mop.setSeqStart(pdbStart); - mop.setSeqEnd(pdbEnd); - mop.setSeqName(seq.getName()); - mop.setSeqResidue(matchedSeq); - - mop.setStrStart(seqStart); - mop.setStrEnd(seqEnd); - mop.setStrName(structId); - mop.setStrResidue(targetStrucSeqs.toString()); - - mop.setType("pep"); - os.print(getMappingOutput(mop).toString()); - } - return mapping; } - /** * * @param chainId @@ -645,9 +619,10 @@ public class SiftsClient implements SiftsClientI * Two dimension array of residue index versus atom position * @throws IllegalArgumentException * Thrown if chainId or mapping is null + * @throws SiftsException */ - void populateAtomPositions(String chainId, - HashMap mapping) throws IllegalArgumentException + void populateAtomPositions(String chainId, Map mapping) + throws IllegalArgumentException, SiftsException { try { @@ -665,9 +640,12 @@ public class SiftsClient implements SiftsClientI map[PDB_ATOM_POS] = getAtomIndex(map[PDB_RES_POS], chain.atoms); } } + } catch (NullPointerException e) + { + throw new SiftsException(e.getMessage()); } catch (Exception e) { - e.printStackTrace(); + throw new SiftsException(e.getMessage()); } } @@ -704,7 +682,7 @@ public class SiftsClient implements SiftsClientI */ private boolean isResidueObserved(Residue residue) { - HashSet annotations = getResidueAnnotaitons(residue, + Set annotations = getResidueAnnotaitons(residue, ResidueDetailType.ANNOTATION); if (annotations == null || annotations.isEmpty()) { @@ -727,7 +705,7 @@ public class SiftsClient implements SiftsClientI * @param type * @return */ - private HashSet getResidueAnnotaitons(Residue residue, + private Set getResidueAnnotaitons(Residue residue, ResidueDetailType type) { HashSet foundAnnotations = new HashSet(); @@ -752,8 +730,9 @@ public class SiftsClient implements SiftsClientI private boolean isFoundInSiftsEntry(String accessionId) { + Set siftsDBRefs = getAllMappingAccession(); return accessionId != null - && getAllMappingAccession().contains(accessionId); + && siftsDBRefs.contains(accessionId.toLowerCase()); } /** @@ -761,15 +740,15 @@ public class SiftsClient implements SiftsClientI * * @param resNumMap */ - void padWithGaps(TreeMap resNumMap, - ArrayList omitNonObserved) + void padWithGaps(Map resNumMap, + List omitNonObserved) { if (resNumMap == null || resNumMap.isEmpty()) { return; } Integer[] keys = resNumMap.keySet().toArray(new Integer[0]); - Arrays.sort(keys); + // Arrays.sort(keys); int firstIndex = keys[0]; int lastIndex = keys[keys.length - 1]; // System.out.println("Min value " + firstIndex); @@ -788,28 +767,8 @@ public class SiftsClient implements SiftsClientI @Override public Entity getEntityById(String id) throws SiftsException { - // Sometimes SIFTS mappings are wrongly swapped between different chains of - // a PDB entry. This results to wrong mappings being generated. The boolean - // flag 'isGetEntityIdDirectly, determines whether an entity to process is - // determined by a greedy heuristic search or by just matching the Chain Id - // directly against the entity Id tag. Setting the default value to 'false' - // utilise the heuristic search which always produces correct mappings but - // less optimised processing, where as changing the value to 'true' - // optimises performance but might result to incorrect mapping in some cases - // where SIFTS mappings are wrongly swapped between different chains. - boolean isGetEntityIdDirectly = false; - if (isGetEntityIdDirectly) - { - List entities = siftsEntry.getEntity(); - for (Entity entity : entities) - { - if (!entity.getEntityId().equalsIgnoreCase(id)) - { - continue; - } - return entity; - } - } + // Determines an entity to process by performing a heuristic matching of all + // Entities with the given chainId and choosing the best matching Entity Entity entity = getEntityByMostOptimalMatchedId(id); if (entity != null) { @@ -858,7 +817,8 @@ public class SiftsClient implements SiftsClientI } } } - sPojo[count].pid = 100 * (sPojo[count].chainIdFreq / sPojo[count].resCount); + sPojo[count].pid = (100 * sPojo[count].chainIdFreq) + / sPojo[count].resCount; ++count; } Arrays.sort(sPojo, Collections.reverseOrder()); @@ -879,7 +839,7 @@ public class SiftsClient implements SiftsClientI return null; } - public class SiftsEntitySortPojo implements + private class SiftsEntitySortPojo implements Comparable { public String entityId; @@ -897,18 +857,79 @@ public class SiftsClient implements SiftsClientI } } - @Override - public String[] getEntryDBs() + private class SegmentHelperPojo { - System.out.println("\nListing DB entries..."); - List availDbs = new ArrayList(); - List dbs = siftsEntry.getListDB().getDb(); - for (Db db : dbs) + private SequenceI seq; + + private HashMap mapping; + + private TreeMap resNumMap; + + private List omitNonObserved; + + private int nonObservedShiftIndex; + + public SegmentHelperPojo(SequenceI seq, + HashMap mapping, + TreeMap resNumMap, + List omitNonObserved, int nonObservedShiftIndex) { - availDbs.add(db.getDbSource()); - System.out.println(db.getDbSource() + " | " + db.getDbCoordSys()); + setSeq(seq); + setMapping(mapping); + setResNumMap(resNumMap); + setOmitNonObserved(omitNonObserved); + setNonObservedShiftIndex(nonObservedShiftIndex); + } + + public SequenceI getSeq() + { + return seq; + } + + public void setSeq(SequenceI seq) + { + this.seq = seq; + } + + public HashMap getMapping() + { + return mapping; + } + + public void setMapping(HashMap mapping) + { + this.mapping = mapping; + } + + public TreeMap getResNumMap() + { + return resNumMap; + } + + public void setResNumMap(TreeMap resNumMap) + { + this.resNumMap = resNumMap; + } + + public List getOmitNonObserved() + { + return omitNonObserved; + } + + public void setOmitNonObserved(List omitNonObserved) + { + this.omitNonObserved = omitNonObserved; + } + + public int getNonObservedShiftIndex() + { + return nonObservedShiftIndex; + } + + public void setNonObservedShiftIndex(int nonObservedShiftIndex) + { + this.nonObservedShiftIndex = nonObservedShiftIndex; } - return availDbs.toArray(new String[0]); } @Override @@ -1031,7 +1052,6 @@ public class SiftsClient implements SiftsClientI output.append("Length of alignment = " + seqRes.length()).append( NEWLINE); output.append(new Format("Percentage ID = %2.2f").form(pid)); - output.append(NEWLINE); return output; } @@ -1054,12 +1074,6 @@ public class SiftsClient implements SiftsClientI } @Override - public String getDbEvidence() - { - return siftsEntry.getDbEvidence(); - } - - @Override public String getDbSource() { return siftsEntry.getDbSource(); diff --git a/test/MCview/AtomTest.java b/test/MCview/AtomTest.java index 33d332a..21a79fe 100644 --- a/test/MCview/AtomTest.java +++ b/test/MCview/AtomTest.java @@ -42,7 +42,7 @@ public class AtomTest assertEquals("GLN", a.resName); assertEquals("A", a.chain); assertEquals(48, a.resNumber); - assertEquals(" 48 ", a.resNumIns); + assertEquals("48", a.resNumIns); assertEquals(' ', a.insCode); assertEquals(22.290, a.x, 0.00001); assertEquals(8.595, a.y, 0.00001); diff --git a/test/MCview/PDBChainTest.java b/test/MCview/PDBChainTest.java index 6ee688f..0da7285 100644 --- a/test/MCview/PDBChainTest.java +++ b/test/MCview/PDBChainTest.java @@ -34,7 +34,7 @@ import jalview.datamodel.SequenceI; import jalview.schemes.Colour; import jalview.schemes.ColourSchemeI; import jalview.schemes.TaylorColourScheme; -import jalview.structure.StructureViewSettings; +import jalview.structure.StructureImportSettings; import java.awt.Color; import java.util.Vector; @@ -58,7 +58,7 @@ public class PDBChainTest public void setUp() { System.out.println("setup"); - StructureViewSettings.setShowSeqFeatures(true); + StructureImportSettings.setShowSeqFeatures(true); c = new PDBChain("1GAQ", "A"); } diff --git a/test/MCview/PDBfileTest.java b/test/MCview/PDBfileTest.java index a6a1de4..8e2e2fe 100644 --- a/test/MCview/PDBfileTest.java +++ b/test/MCview/PDBfileTest.java @@ -26,6 +26,7 @@ import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; +import jalview.bin.Cache; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; @@ -33,10 +34,12 @@ import jalview.datamodel.PDBEntry; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.io.AppletFormatAdapter; +import jalview.structure.StructureImportSettings; import java.io.IOException; import java.util.List; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class PDBfileTest @@ -307,4 +310,19 @@ public class PDBfileTest pf.addAnnotations(al); return al.getAlignmentAnnotation(); } + + //@formatter:on + + @BeforeMethod(alwaysRun = true) + public void setUp() + { + Cache.loadProperties("test/jalview/io/testProps.jvprops"); + Cache.applicationProperties.setProperty("STRUCT_FROM_PDB", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("ADD_TEMPFACT_ANN", + Boolean.TRUE.toString()); + Cache.applicationProperties.setProperty("ADD_SS_ANN", + Boolean.TRUE.toString()); + StructureImportSettings.setDefaultStructureFileFormat("PDB"); + } } diff --git a/test/jalview/analysis/AlignmentUtilsTests.java b/test/jalview/analysis/AlignmentUtilsTests.java index 860d979..ddd38e7 100644 --- a/test/jalview/analysis/AlignmentUtilsTests.java +++ b/test/jalview/analysis/AlignmentUtilsTests.java @@ -22,6 +22,7 @@ package jalview.analysis; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; @@ -47,7 +48,6 @@ import jalview.util.MappingUtils; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -974,117 +974,184 @@ public class AlignmentUtilsTests @Test(groups = { "Functional" }) public void testMakeCdsAlignment() { + /* + * scenario: + * dna1 --> [4, 6] [10,12] --> pep1 + * dna2 --> [1, 3] [7, 9] [13,15] --> pep1 + */ SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa"); SequenceI dna2 = new Sequence("dna2", "GGGcccTTTaaaCCC"); SequenceI pep1 = new Sequence("pep1", "GF"); SequenceI pep2 = new Sequence("pep2", "GFP"); + pep1.addDBRef(new DBRefEntry("UNIPROT", "0", "pep1")); + pep2.addDBRef(new DBRefEntry("UNIPROT", "0", "pep2")); dna1.createDatasetSequence(); dna2.createDatasetSequence(); pep1.createDatasetSequence(); pep2.createDatasetSequence(); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds1", 4, 6, 0f, - null)); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds2", 10, 12, 0f, - null)); - dna2.addSequenceFeature(new SequenceFeature("CDS", "cds3", 1, 3, 0f, - null)); - dna2.addSequenceFeature(new SequenceFeature("CDS", "cds4", 7, 9, 0f, - null)); - dna2.addSequenceFeature(new SequenceFeature("CDS", "cds5", 13, 15, 0f, - null)); AlignmentI dna = new Alignment(new SequenceI[] { dna1, dna2 }); dna.setDataset(null); - List mappings = new ArrayList(); - MapList map = new MapList(new int[] { 4, 6, 10, 12 }, + /* + * need a sourceDbRef if we are to construct dbrefs to the CDS + * sequence from the dna contig sequences + */ + DBRefEntry dbref = new DBRefEntry("ENSEMBL", "0", "dna1"); + dna1.getDatasetSequence().addDBRef(dbref); + org.testng.Assert.assertEquals(dbref, dna1.getPrimaryDBRefs().get(0)); + dbref = new DBRefEntry("ENSEMBL", "0", "dna2"); + dna2.getDatasetSequence().addDBRef(dbref); + org.testng.Assert.assertEquals(dbref, dna2.getPrimaryDBRefs().get(0)); + + /* + * CDS sequences are 'discovered' from dna-to-protein mappings on the alignment + * dataset (e.g. added from dbrefs by CrossRef.findXrefSequences) + */ + MapList mapfordna1 = new MapList(new int[] { 4, 6, 10, 12 }, new int[] { 1, 2 }, 3, 1); AlignedCodonFrame acf = new AlignedCodonFrame(); - acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map); - mappings.add(acf); - map = new MapList(new int[] { 1, 3, 7, 9, 13, 15 }, new int[] { 1, 3 }, + acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), + mapfordna1); + dna.addCodonFrame(acf); + MapList mapfordna2 = new MapList(new int[] { 1, 3, 7, 9, 13, 15 }, + new int[] { 1, 3 }, 3, 1); acf = new AlignedCodonFrame(); - acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map); - mappings.add(acf); + acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), + mapfordna2); + dna.addCodonFrame(acf); + + /* + * In this case, mappings originally came from matching Uniprot accessions - so need an xref on dna involving those regions. These are normally constructed from CDS annotation + */ + DBRefEntry dna1xref = new DBRefEntry("UNIPROT", "ENSEMBL", "pep1", + new Mapping(mapfordna1)); + dna1.getDatasetSequence().addDBRef(dna1xref); + DBRefEntry dna2xref = new DBRefEntry("UNIPROT", "ENSEMBL", "pep2", + new Mapping(mapfordna2)); + dna2.getDatasetSequence().addDBRef(dna2xref); /* * execute method under test: */ AlignmentI cds = AlignmentUtils.makeCdsAlignment(new SequenceI[] { - dna1, dna2 }, mappings, dna); + dna1, dna2 }, dna.getDataset(), null); + /* + * verify cds sequences + */ assertEquals(2, cds.getSequences().size()); - assertEquals("GGGTTT", cds.getSequenceAt(0) - .getSequenceAsString()); - assertEquals("GGGTTTCCC", cds.getSequenceAt(1) - .getSequenceAsString()); + assertEquals("GGGTTT", cds.getSequenceAt(0).getSequenceAsString()); + assertEquals("GGGTTTCCC", cds.getSequenceAt(1).getSequenceAsString()); /* * verify shared, extended alignment dataset */ assertSame(dna.getDataset(), cds.getDataset()); - assertTrue(dna.getDataset().getSequences() - .contains(cds.getSequenceAt(0).getDatasetSequence())); - assertTrue(dna.getDataset().getSequences() - .contains(cds.getSequenceAt(1).getDatasetSequence())); + SequenceI cds1Dss = cds.getSequenceAt(0).getDatasetSequence(); + SequenceI cds2Dss = cds.getSequenceAt(1).getDatasetSequence(); + assertTrue(dna.getDataset().getSequences().contains(cds1Dss)); + assertTrue(dna.getDataset().getSequences().contains(cds2Dss)); + + /* + * verify CDS has a dbref with mapping to peptide + */ + assertNotNull(cds1Dss.getDBRefs()); + assertEquals(2, cds1Dss.getDBRefs().length); + dbref = cds1Dss.getDBRefs()[0]; + assertEquals(dna1xref.getSource(), dbref.getSource()); + // version is via ensembl's primary ref + assertEquals(dna1xref.getVersion(), dbref.getVersion()); + assertEquals(dna1xref.getAccessionId(), dbref.getAccessionId()); + assertNotNull(dbref.getMap()); + assertSame(pep1.getDatasetSequence(), dbref.getMap().getTo()); + MapList cdsMapping = new MapList(new int[] { 1, 6 }, + new int[] { 1, 2 }, 3, 1); + assertEquals(cdsMapping, dbref.getMap().getMap()); /* - * Verify mappings from CDS to peptide and cDNA to CDS + * verify peptide has added a dbref with reverse mapping to CDS + */ + assertNotNull(pep1.getDBRefs()); + // FIXME pep1.getDBRefs() is 1 - is that the correct behaviour ? + assertEquals(2, pep1.getDBRefs().length); + dbref = pep1.getDBRefs()[1]; + assertEquals("ENSEMBL", dbref.getSource()); + assertEquals("0", dbref.getVersion()); + assertEquals("CDS|dna1", dbref.getAccessionId()); + assertNotNull(dbref.getMap()); + assertSame(cds1Dss, dbref.getMap().getTo()); + assertEquals(cdsMapping.getInverse(), dbref.getMap().getMap()); + + /* + * Verify mappings from CDS to peptide, cDNA to CDS, and cDNA to peptide * the mappings are on the shared alignment dataset + * 6 mappings, 2*(DNA->CDS), 2*(DNA->Pep), 2*(CDS->Pep) */ - assertSame(dna.getCodonFrames(), cds.getCodonFrames()); - List cdsMappings = cds.getCodonFrames(); - assertEquals(2, cdsMappings.size()); - + List cdsMappings = cds.getDataset().getCodonFrames(); + assertEquals(6, cdsMappings.size()); + + /* + * verify that mapping sets for dna and cds alignments are different + * [not current behaviour - all mappings are on the alignment dataset] + */ + // select -> subselect type to test. + // Assert.assertNotSame(dna.getCodonFrames(), cds.getCodonFrames()); + // assertEquals(4, dna.getCodonFrames().size()); + // assertEquals(4, cds.getCodonFrames().size()); + /* + * Two mappings involve pep1 (dna to pep1, cds to pep1) * Mapping from pep1 to GGGTTT in first new exon sequence */ - List pep1Mapping = MappingUtils + List pep1Mappings = MappingUtils .findMappingsForSequence(pep1, cdsMappings); - assertEquals(1, pep1Mapping.size()); + assertEquals(2, pep1Mappings.size()); + List mappings = MappingUtils + .findMappingsForSequence(cds.getSequenceAt(0), pep1Mappings); + assertEquals(1, mappings.size()); + // map G to GGG - SearchResults sr = MappingUtils - .buildSearchResults(pep1, 1, cdsMappings); + SearchResults sr = MappingUtils.buildSearchResults(pep1, 1, mappings); assertEquals(1, sr.getResults().size()); Match m = sr.getResults().get(0); - assertSame(cds.getSequenceAt(0).getDatasetSequence(), - m.getSequence()); + assertSame(cds1Dss, m.getSequence()); assertEquals(1, m.getStart()); assertEquals(3, m.getEnd()); // map F to TTT - sr = MappingUtils.buildSearchResults(pep1, 2, cdsMappings); + sr = MappingUtils.buildSearchResults(pep1, 2, mappings); m = sr.getResults().get(0); - assertSame(cds.getSequenceAt(0).getDatasetSequence(), - m.getSequence()); + assertSame(cds1Dss, m.getSequence()); assertEquals(4, m.getStart()); assertEquals(6, m.getEnd()); /* - * Mapping from pep2 to GGGTTTCCC in second new exon sequence + * Two mappings involve pep2 (dna to pep2, cds to pep2) + * Verify mapping from pep2 to GGGTTTCCC in second new exon sequence */ - List pep2Mapping = MappingUtils + List pep2Mappings = MappingUtils .findMappingsForSequence(pep2, cdsMappings); - assertEquals(1, pep2Mapping.size()); + assertEquals(2, pep2Mappings.size()); + mappings = MappingUtils.findMappingsForSequence(cds.getSequenceAt(1), + pep2Mappings); + assertEquals(1, mappings.size()); // map G to GGG - sr = MappingUtils.buildSearchResults(pep2, 1, cdsMappings); + sr = MappingUtils.buildSearchResults(pep2, 1, mappings); assertEquals(1, sr.getResults().size()); m = sr.getResults().get(0); - assertSame(cds.getSequenceAt(1).getDatasetSequence(), - m.getSequence()); + assertSame(cds2Dss, m.getSequence()); assertEquals(1, m.getStart()); assertEquals(3, m.getEnd()); // map F to TTT - sr = MappingUtils.buildSearchResults(pep2, 2, cdsMappings); + sr = MappingUtils.buildSearchResults(pep2, 2, mappings); m = sr.getResults().get(0); - assertSame(cds.getSequenceAt(1).getDatasetSequence(), - m.getSequence()); + assertSame(cds2Dss, m.getSequence()); assertEquals(4, m.getStart()); assertEquals(6, m.getEnd()); // map P to CCC - sr = MappingUtils.buildSearchResults(pep2, 3, cdsMappings); + sr = MappingUtils.buildSearchResults(pep2, 3, mappings); m = sr.getResults().get(0); - assertSame(cds.getSequenceAt(1).getDatasetSequence(), - m.getSequence()); + assertSame(cds2Dss, m.getSequence()); assertEquals(7, m.getStart()); assertEquals(9, m.getEnd()); } @@ -1105,18 +1172,6 @@ public class AlignmentUtilsTests pep1.createDatasetSequence(); pep2.createDatasetSequence(); pep3.createDatasetSequence(); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds1", 4, 6, 0f, - null)); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds2", 10, 12, 0f, - null)); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds3", 1, 3, 0f, - null)); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds4", 7, 9, 0f, - null)); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds5", 1, 3, 0f, - null)); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds6", 10, 12, 0f, - null)); pep1.getDatasetSequence().addDBRef( new DBRefEntry("EMBLCDS", "2", "A12345")); pep2.getDatasetSequence().addDBRef( @@ -1125,40 +1180,38 @@ public class AlignmentUtilsTests new DBRefEntry("EMBLCDS", "4", "A12347")); /* + * Create the CDS alignment + */ + AlignmentI dna = new Alignment(new SequenceI[] { dna1 }); + dna.setDataset(null); + + /* * Make the mappings from dna to protein */ - List mappings = new ArrayList(); // map ...GGG...TTT to GF MapList map = new MapList(new int[] { 4, 6, 10, 12 }, new int[] { 1, 2 }, 3, 1); AlignedCodonFrame acf = new AlignedCodonFrame(); acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map); - mappings.add(acf); + dna.addCodonFrame(acf); // map aaa...ccc to KP map = new MapList(new int[] { 1, 3, 7, 9 }, new int[] { 1, 2 }, 3, 1); acf = new AlignedCodonFrame(); acf.addMap(dna1.getDatasetSequence(), pep2.getDatasetSequence(), map); - mappings.add(acf); + dna.addCodonFrame(acf); // map aaa......TTT to KF map = new MapList(new int[] { 1, 3, 10, 12 }, new int[] { 1, 2 }, 3, 1); acf = new AlignedCodonFrame(); acf.addMap(dna1.getDatasetSequence(), pep3.getDatasetSequence(), map); - mappings.add(acf); - - /* - * Create the CDS alignment; also augments the dna-to-protein mappings with - * exon-to-protein and exon-to-dna mappings - */ - AlignmentI dna = new Alignment(new SequenceI[] { dna1 }); - dna.setDataset(null); + dna.addCodonFrame(acf); /* * execute method under test */ AlignmentI cdsal = AlignmentUtils.makeCdsAlignment( - new SequenceI[] { dna1 }, mappings, dna); + new SequenceI[] { dna1 }, dna.getDataset(), null); /* * Verify we have 3 cds sequences, mapped to pep1/2/3 respectively @@ -1183,7 +1236,7 @@ public class AlignmentUtilsTests SequenceI cdsSeq = cds.get(0); assertEquals("GGGTTT", cdsSeq.getSequenceAsString()); // assertEquals("dna1|A12345", cdsSeq.getName()); - assertEquals("dna1|pep1", cdsSeq.getName()); + assertEquals("CDS|dna1", cdsSeq.getName()); // assertEquals(1, cdsSeq.getDBRefs().length); // DBRefEntry cdsRef = cdsSeq.getDBRefs()[0]; // assertEquals("EMBLCDS", cdsRef.getSource()); @@ -1193,7 +1246,7 @@ public class AlignmentUtilsTests cdsSeq = cds.get(1); assertEquals("aaaccc", cdsSeq.getSequenceAsString()); // assertEquals("dna1|A12346", cdsSeq.getName()); - assertEquals("dna1|pep2", cdsSeq.getName()); + assertEquals("CDS|dna1", cdsSeq.getName()); // assertEquals(1, cdsSeq.getDBRefs().length); // cdsRef = cdsSeq.getDBRefs()[0]; // assertEquals("EMBLCDS", cdsRef.getSource()); @@ -1203,7 +1256,7 @@ public class AlignmentUtilsTests cdsSeq = cds.get(2); assertEquals("aaaTTT", cdsSeq.getSequenceAsString()); // assertEquals("dna1|A12347", cdsSeq.getName()); - assertEquals("dna1|pep3", cdsSeq.getName()); + assertEquals("CDS|dna1", cdsSeq.getName()); // assertEquals(1, cdsSeq.getDBRefs().length); // cdsRef = cdsSeq.getDBRefs()[0]; // assertEquals("EMBLCDS", cdsRef.getSource()); @@ -1214,41 +1267,74 @@ public class AlignmentUtilsTests * Verify there are mappings from each cds sequence to its protein product * and also to its dna source */ - Iterator newMappingsIterator = cdsal - .getCodonFrames().iterator(); + List newMappings = cdsal.getCodonFrames(); - // mappings for dna1 - exon1 - pep1 - AlignedCodonFrame cdsMapping = newMappingsIterator.next(); - List dnaMappings = cdsMapping.getMappingsFromSequence(dna1); - assertEquals(3, dnaMappings.size()); - assertSame(cds.get(0).getDatasetSequence(), dnaMappings.get(0) - .getTo()); - assertEquals("G(1) in CDS should map to G(4) in DNA", 4, dnaMappings - .get(0).getMap().getToPosition(1)); - List peptideMappings = cdsMapping.getMappingsFromSequence(cds - .get(0).getDatasetSequence()); - assertEquals(1, peptideMappings.size()); - assertSame(pep1.getDatasetSequence(), peptideMappings.get(0).getTo()); - - // mappings for dna1 - cds2 - pep2 - assertSame(cds.get(1).getDatasetSequence(), dnaMappings.get(1) - .getTo()); - assertEquals("c(4) in CDS should map to c(7) in DNA", 7, dnaMappings - .get(1).getMap().getToPosition(4)); - peptideMappings = cdsMapping.getMappingsFromSequence(cds.get(1) - .getDatasetSequence()); - assertEquals(1, peptideMappings.size()); - assertSame(pep2.getDatasetSequence(), peptideMappings.get(0).getTo()); - - // mappings for dna1 - cds3 - pep3 - assertSame(cds.get(2).getDatasetSequence(), dnaMappings.get(2) + /* + * 6 mappings involve dna1 (to pep1/2/3, cds1/2/3) + */ + List dnaMappings = MappingUtils + .findMappingsForSequence(dna1, newMappings); + assertEquals(6, dnaMappings.size()); + + /* + * dna1 to pep1 + */ + List mappings = MappingUtils + .findMappingsForSequence(pep1, dnaMappings); + assertEquals(1, mappings.size()); + assertEquals(1, mappings.get(0).getMappings().size()); + assertSame(pep1.getDatasetSequence(), mappings.get(0).getMappings() + .get(0).getMapping().getTo()); + + /* + * dna1 to cds1 + */ + List dnaToCds1Mappings = MappingUtils + .findMappingsForSequence(cds.get(0), dnaMappings); + Mapping mapping = dnaToCds1Mappings.get(0).getMappings().get(0) + .getMapping(); + assertSame(cds.get(0).getDatasetSequence(), mapping .getTo()); - assertEquals("T(4) in CDS should map to T(10) in DNA", 10, dnaMappings - .get(2).getMap().getToPosition(4)); - peptideMappings = cdsMapping.getMappingsFromSequence(cds.get(2) - .getDatasetSequence()); - assertEquals(1, peptideMappings.size()); - assertSame(pep3.getDatasetSequence(), peptideMappings.get(0).getTo()); + assertEquals("G(1) in CDS should map to G(4) in DNA", 4, mapping + .getMap().getToPosition(1)); + + /* + * dna1 to pep2 + */ + mappings = MappingUtils.findMappingsForSequence(pep2, dnaMappings); + assertEquals(1, mappings.size()); + assertEquals(1, mappings.get(0).getMappings().size()); + assertSame(pep2.getDatasetSequence(), mappings.get(0).getMappings() + .get(0).getMapping().getTo()); + + /* + * dna1 to cds2 + */ + List dnaToCds2Mappings = MappingUtils + .findMappingsForSequence(cds.get(1), dnaMappings); + mapping = dnaToCds2Mappings.get(0).getMappings().get(0).getMapping(); + assertSame(cds.get(1).getDatasetSequence(), mapping.getTo()); + assertEquals("c(4) in CDS should map to c(7) in DNA", 7, mapping + .getMap().getToPosition(4)); + + /* + * dna1 to pep3 + */ + mappings = MappingUtils.findMappingsForSequence(pep3, dnaMappings); + assertEquals(1, mappings.size()); + assertEquals(1, mappings.get(0).getMappings().size()); + assertSame(pep3.getDatasetSequence(), mappings.get(0).getMappings() + .get(0).getMapping().getTo()); + + /* + * dna1 to cds3 + */ + List dnaToCds3Mappings = MappingUtils + .findMappingsForSequence(cds.get(2), dnaMappings); + mapping = dnaToCds3Mappings.get(0).getMappings().get(0).getMapping(); + assertSame(cds.get(2).getDatasetSequence(), mapping.getTo()); + assertEquals("T(4) in CDS should map to T(10) in DNA", 10, mapping + .getMap().getToPosition(4)); } @Test(groups = { "Functional" }) @@ -1497,36 +1583,24 @@ public class AlignmentUtilsTests dna3.createDatasetSequence(); pep1.createDatasetSequence(); pep2.createDatasetSequence(); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds1", 4, 8, 0f, - null)); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds2", 9, 12, 0f, - null)); - dna1.addSequenceFeature(new SequenceFeature("CDS", "cds3", 16, 18, 0f, - null)); - dna2.addSequenceFeature(new SequenceFeature("CDS", "cds", 4, 8, 0f, - null)); - dna2.addSequenceFeature(new SequenceFeature("CDS", "cds", 12, 12, 0f, - null)); - dna2.addSequenceFeature(new SequenceFeature("CDS", "cds", 16, 18, 0f, - null)); + + AlignmentI dna = new Alignment(new SequenceI[] { dna1, dna2, dna3 }); + dna.setDataset(null); - List mappings = new ArrayList(); MapList map = new MapList(new int[] { 4, 12, 16, 18 }, new int[] { 1, 4 }, 3, 1); AlignedCodonFrame acf = new AlignedCodonFrame(); acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map); - mappings.add(acf); + dna.addCodonFrame(acf); map = new MapList(new int[] { 4, 8, 12, 12, 16, 18 }, new int[] { 1, 3 }, 3, 1); acf = new AlignedCodonFrame(); acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map); - mappings.add(acf); + dna.addCodonFrame(acf); - AlignmentI dna = new Alignment(new SequenceI[] { dna1, dna2, dna3 }); - dna.setDataset(null); AlignmentI cds = AlignmentUtils.makeCdsAlignment(new SequenceI[] { - dna1, dna2, dna3 }, mappings, dna); + dna1, dna2, dna3 }, dna.getDataset(), null); List cdsSeqs = cds.getSequences(); assertEquals(2, cdsSeqs.size()); assertEquals("GGGCCCTTTGGG", cdsSeqs.get(0).getSequenceAsString()); @@ -1542,59 +1616,69 @@ public class AlignmentUtilsTests .contains(cdsSeqs.get(1).getDatasetSequence())); /* - * Verify updated mappings + * Verify 6 mappings: dna1 to cds1, cds1 to pep1, dna1 to pep1 + * and the same for dna2/cds2/pep2 */ - List cdsMappings = cds.getCodonFrames(); - assertEquals(2, cdsMappings.size()); + List mappings = cds.getCodonFrames(); + assertEquals(6, mappings.size()); /* - * Mapping from pep1 to GGGTTT in first new CDS sequence + * 2 mappings involve pep1 */ - List pep1Mapping = MappingUtils - .findMappingsForSequence(pep1, cdsMappings); - assertEquals(1, pep1Mapping.size()); + List pep1Mappings = MappingUtils + .findMappingsForSequence(pep1, mappings); + assertEquals(2, pep1Mappings.size()); + /* + * Get mapping of pep1 to cds1 and verify it * maps GPFG to 1-3,4-6,7-9,10-12 */ - SearchResults sr = MappingUtils - .buildSearchResults(pep1, 1, cdsMappings); + List pep1CdsMappings = MappingUtils + .findMappingsForSequence(cds.getSequenceAt(0), pep1Mappings); + assertEquals(1, pep1CdsMappings.size()); + SearchResults sr = MappingUtils.buildSearchResults(pep1, 1, + pep1CdsMappings); assertEquals(1, sr.getResults().size()); Match m = sr.getResults().get(0); assertEquals(cds.getSequenceAt(0).getDatasetSequence(), m.getSequence()); assertEquals(1, m.getStart()); assertEquals(3, m.getEnd()); - sr = MappingUtils.buildSearchResults(pep1, 2, cdsMappings); + sr = MappingUtils.buildSearchResults(pep1, 2, pep1CdsMappings); m = sr.getResults().get(0); assertEquals(4, m.getStart()); assertEquals(6, m.getEnd()); - sr = MappingUtils.buildSearchResults(pep1, 3, cdsMappings); + sr = MappingUtils.buildSearchResults(pep1, 3, pep1CdsMappings); m = sr.getResults().get(0); assertEquals(7, m.getStart()); assertEquals(9, m.getEnd()); - sr = MappingUtils.buildSearchResults(pep1, 4, cdsMappings); + sr = MappingUtils.buildSearchResults(pep1, 4, pep1CdsMappings); m = sr.getResults().get(0); assertEquals(10, m.getStart()); assertEquals(12, m.getEnd()); /* - * GPG in pep2 map to 1-3,4-6,7-9 in second CDS sequence + * Get mapping of pep2 to cds2 and verify it + * maps GPG in pep2 to 1-3,4-6,7-9 in second CDS sequence */ - List pep2Mapping = MappingUtils - .findMappingsForSequence(pep2, cdsMappings); - assertEquals(1, pep2Mapping.size()); - sr = MappingUtils.buildSearchResults(pep2, 1, cdsMappings); + List pep2Mappings = MappingUtils + .findMappingsForSequence(pep2, mappings); + assertEquals(2, pep2Mappings.size()); + List pep2CdsMappings = MappingUtils + .findMappingsForSequence(cds.getSequenceAt(1), pep2Mappings); + assertEquals(1, pep2CdsMappings.size()); + sr = MappingUtils.buildSearchResults(pep2, 1, pep2CdsMappings); assertEquals(1, sr.getResults().size()); m = sr.getResults().get(0); assertEquals(cds.getSequenceAt(1).getDatasetSequence(), m.getSequence()); assertEquals(1, m.getStart()); assertEquals(3, m.getEnd()); - sr = MappingUtils.buildSearchResults(pep2, 2, cdsMappings); + sr = MappingUtils.buildSearchResults(pep2, 2, pep2CdsMappings); m = sr.getResults().get(0); assertEquals(4, m.getStart()); assertEquals(6, m.getEnd()); - sr = MappingUtils.buildSearchResults(pep2, 3, cdsMappings); + sr = MappingUtils.buildSearchResults(pep2, 3, pep2CdsMappings); m = sr.getResults().get(0); assertEquals(7, m.getStart()); assertEquals(9, m.getEnd()); @@ -1870,13 +1954,15 @@ public class AlignmentUtilsTests public void testComputePeptideVariants() { /* - * scenario: AAATTTCCC codes for KFP, with variants - * GAA -> E - * CAA -> Q - * AAG synonymous - * AAT -> N - * TTC synonymous - * CAC,CGC -> H,R (as one variant) + * scenario: AAATTTCCC codes for KFP + * variants: + * GAA -> E source: Ensembl + * CAA -> Q source: dbSNP + * AAG synonymous source: COSMIC + * AAT -> N source: Ensembl + * ...TTC synonymous source: dbSNP + * ......CAC,CGC -> H,R source: COSMIC + * (one variant with two alleles) */ SequenceI peptide = new Sequence("pep/10-12", "KFP"); @@ -1884,32 +1970,35 @@ public class AlignmentUtilsTests * two distinct variants for codon 1 position 1 * second one has clinical significance */ + String ensembl = "Ensembl"; + String dbSnp = "dbSNP"; + String cosmic = "COSMIC"; SequenceFeature sf1 = new SequenceFeature("sequence_variant", "", 1, 1, - 0f, null); + 0f, ensembl); sf1.setValue("alleles", "A,G"); // GAA -> E sf1.setValue("ID", "var1.125A>G"); SequenceFeature sf2 = new SequenceFeature("sequence_variant", "", 1, 1, - 0f, null); + 0f, dbSnp); 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); + 0f, cosmic); 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); + 0f, ensembl); 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); + 0f, dbSnp); 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); + 0f, cosmic); sf6.setValue("alleles", "C,A,G"); // CAC,CGC -> H,R sf6.setValue("ID", "var6"); sf6.setValue("clinical_significance", "Good"); @@ -1957,71 +2046,81 @@ public class AlignmentUtilsTests /* * verify added sequence features for - * var1 K -> E - * var2 K -> Q - * var4 K -> N - * var6 P -> H - * var6 P -> R + * var1 K -> E Ensembl + * var2 K -> Q dbSNP + * var4 K -> N Ensembl + * var6 P -> H COSMIC + * var6 P -> R COSMIC */ SequenceFeature[] sfs = peptide.getSequenceFeatures(); assertEquals(5, sfs.length); + SequenceFeature sf = sfs[0]; assertEquals(1, sf.getBegin()); assertEquals(1, sf.getEnd()); - assertEquals("K->E", sf.getDescription()); + 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( - "K->E var1.125A>G|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var1.125A%3EG", + "p.Lys1Glu var1.125A>G|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var1.125A%3EG", sf.links.get(0)); + assertEquals(ensembl, sf.getFeatureGroup()); + sf = sfs[1]; assertEquals(1, sf.getBegin()); assertEquals(1, sf.getEnd()); - assertEquals("K->Q", sf.getDescription()); + 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( - "K->Q var2|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var2", + "p.Lys1Gln var2|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var2", sf.links.get(0)); + assertEquals(dbSnp, sf.getFeatureGroup()); + sf = sfs[2]; assertEquals(1, sf.getBegin()); assertEquals(1, sf.getEnd()); - assertEquals("K->N", sf.getDescription()); + 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( - "K->N var4|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var4", + "p.Lys1Asn var4|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var4", sf.links.get(0)); + assertEquals(ensembl, sf.getFeatureGroup()); + + // var5 generates two distinct protein variant features sf = sfs[3]; assertEquals(3, sf.getBegin()); assertEquals(3, sf.getEnd()); - assertEquals("P->H", sf.getDescription()); + 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->H var6|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var6", + "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(cosmic, sf.getFeatureGroup()); + sf = sfs[4]; assertEquals(3, sf.getBegin()); assertEquals(3, sf.getEnd()); - assertEquals("P->R", sf.getDescription()); + 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->R var6|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var6", + "p.Pro3Arg var6|http://www.ensembl.org/Homo_sapiens/Variation/Summary?v=var6", sf.links.get(0)); + assertEquals(cosmic, sf.getFeatureGroup()); } /** @@ -2188,4 +2287,219 @@ public class AlignmentUtilsTests assertEquals('T', map.get(11).get(seq1).charValue()); assertEquals('T', map.get(12).get(seq1).charValue()); } + + /** + * Test for the case where the products for which we want CDS are specified. + * This is to represent the case where EMBL has CDS mappings to both Uniprot + * and EMBLCDSPROTEIN. makeCdsAlignment() should only return the mappings for + * the protein sequences specified. + */ + @Test(groups = { "Functional" }) + public void testMakeCdsAlignment_filterProducts() + { + SequenceI dna1 = new Sequence("dna1", "aaaGGGcccTTTaaa"); + SequenceI dna2 = new Sequence("dna2", "GGGcccTTTaaaCCC"); + SequenceI pep1 = new Sequence("Uniprot|pep1", "GF"); + SequenceI pep2 = new Sequence("Uniprot|pep2", "GFP"); + SequenceI pep3 = new Sequence("EMBL|pep3", "GF"); + SequenceI pep4 = new Sequence("EMBL|pep4", "GFP"); + dna1.createDatasetSequence(); + dna2.createDatasetSequence(); + pep1.createDatasetSequence(); + pep2.createDatasetSequence(); + pep3.createDatasetSequence(); + pep4.createDatasetSequence(); + AlignmentI dna = new Alignment(new SequenceI[] { dna1, dna2 }); + dna.setDataset(null); + AlignmentI emblPeptides = new Alignment(new SequenceI[] { pep3, pep4 }); + emblPeptides.setDataset(null); + + AlignedCodonFrame acf = new AlignedCodonFrame(); + MapList map = new MapList(new int[] { 4, 6, 10, 12 }, + new int[] { 1, 2 }, 3, 1); + acf.addMap(dna1.getDatasetSequence(), pep1.getDatasetSequence(), map); + acf.addMap(dna1.getDatasetSequence(), pep3.getDatasetSequence(), map); + dna.addCodonFrame(acf); + + acf = new AlignedCodonFrame(); + map = new MapList(new int[] { 1, 3, 7, 9, 13, 15 }, new int[] { 1, 3 }, + 3, 1); + acf.addMap(dna2.getDatasetSequence(), pep2.getDatasetSequence(), map); + acf.addMap(dna2.getDatasetSequence(), pep4.getDatasetSequence(), map); + dna.addCodonFrame(acf); + + /* + * execute method under test to find CDS for EMBL peptides only + */ + AlignmentI cds = AlignmentUtils.makeCdsAlignment(new SequenceI[] { + dna1, dna2 }, dna.getDataset(), emblPeptides.getSequencesArray()); + + assertEquals(2, cds.getSequences().size()); + assertEquals("GGGTTT", cds.getSequenceAt(0).getSequenceAsString()); + assertEquals("GGGTTTCCC", cds.getSequenceAt(1).getSequenceAsString()); + + /* + * verify shared, extended alignment dataset + */ + assertSame(dna.getDataset(), cds.getDataset()); + assertTrue(dna.getDataset().getSequences() + .contains(cds.getSequenceAt(0).getDatasetSequence())); + assertTrue(dna.getDataset().getSequences() + .contains(cds.getSequenceAt(1).getDatasetSequence())); + + /* + * Verify mappings from CDS to peptide, cDNA to CDS, and cDNA to peptide + * the mappings are on the shared alignment dataset + */ + List cdsMappings = cds.getDataset().getCodonFrames(); + /* + * 6 mappings, 2*(DNA->CDS), 2*(DNA->Pep), 2*(CDS->Pep) + */ + assertEquals(6, cdsMappings.size()); + + /* + * verify that mapping sets for dna and cds alignments are different + * [not current behaviour - all mappings are on the alignment dataset] + */ + // select -> subselect type to test. + // Assert.assertNotSame(dna.getCodonFrames(), cds.getCodonFrames()); + // assertEquals(4, dna.getCodonFrames().size()); + // assertEquals(4, cds.getCodonFrames().size()); + + /* + * Two mappings involve pep3 (dna to pep3, cds to pep3) + * Mapping from pep3 to GGGTTT in first new exon sequence + */ + List pep3Mappings = MappingUtils + .findMappingsForSequence(pep3, cdsMappings); + assertEquals(2, pep3Mappings.size()); + List mappings = MappingUtils + .findMappingsForSequence(cds.getSequenceAt(0), pep3Mappings); + assertEquals(1, mappings.size()); + + // map G to GGG + SearchResults sr = MappingUtils.buildSearchResults(pep3, 1, mappings); + assertEquals(1, sr.getResults().size()); + Match m = sr.getResults().get(0); + assertSame(cds.getSequenceAt(0).getDatasetSequence(), m.getSequence()); + assertEquals(1, m.getStart()); + assertEquals(3, m.getEnd()); + // map F to TTT + sr = MappingUtils.buildSearchResults(pep3, 2, mappings); + m = sr.getResults().get(0); + assertSame(cds.getSequenceAt(0).getDatasetSequence(), m.getSequence()); + assertEquals(4, m.getStart()); + assertEquals(6, m.getEnd()); + + /* + * Two mappings involve pep4 (dna to pep4, cds to pep4) + * Verify mapping from pep4 to GGGTTTCCC in second new exon sequence + */ + List pep4Mappings = MappingUtils + .findMappingsForSequence(pep4, cdsMappings); + assertEquals(2, pep4Mappings.size()); + mappings = MappingUtils.findMappingsForSequence(cds.getSequenceAt(1), + pep4Mappings); + assertEquals(1, mappings.size()); + // map G to GGG + sr = MappingUtils.buildSearchResults(pep4, 1, mappings); + assertEquals(1, sr.getResults().size()); + m = sr.getResults().get(0); + assertSame(cds.getSequenceAt(1).getDatasetSequence(), m.getSequence()); + assertEquals(1, m.getStart()); + assertEquals(3, m.getEnd()); + // map F to TTT + sr = MappingUtils.buildSearchResults(pep4, 2, mappings); + m = sr.getResults().get(0); + assertSame(cds.getSequenceAt(1).getDatasetSequence(), m.getSequence()); + assertEquals(4, m.getStart()); + assertEquals(6, m.getEnd()); + // map P to CCC + sr = MappingUtils.buildSearchResults(pep4, 3, mappings); + m = sr.getResults().get(0); + assertSame(cds.getSequenceAt(1).getDatasetSequence(), m.getSequence()); + assertEquals(7, m.getStart()); + assertEquals(9, m.getEnd()); + } + + /** + * Test the method that just copies aligned sequences, provided all sequences + * to be aligned share the aligned sequence's dataset + */ + @Test(groups = "Functional") + public void testAlignAsSameSequences() + { + SequenceI dna1 = new Sequence("dna1", "cccGGGTTTaaa"); + SequenceI dna2 = new Sequence("dna2", "CCCgggtttAAA"); + AlignmentI al1 = new Alignment(new SequenceI[] { dna1, dna2 }); + ((Alignment) al1).createDatasetAlignment(); + + SequenceI dna3 = new Sequence(dna1); + SequenceI dna4 = new Sequence(dna2); + assertSame(dna3.getDatasetSequence(), dna1.getDatasetSequence()); + assertSame(dna4.getDatasetSequence(), dna2.getDatasetSequence()); + String seq1 = "-cc-GG-GT-TT--aaa"; + dna3.setSequence(seq1); + String seq2 = "C--C-Cgg--gtt-tAA-A-"; + dna4.setSequence(seq2); + AlignmentI al2 = new Alignment(new SequenceI[] { dna3, dna4 }); + ((Alignment) al2).createDatasetAlignment(); + + assertTrue(AlignmentUtils.alignAsSameSequences(al1, al2)); + assertEquals(seq1, al1.getSequenceAt(0).getSequenceAsString()); + assertEquals(seq2, al1.getSequenceAt(1).getSequenceAsString()); + + /* + * add another sequence to 'aligned' - should still succeed, since + * unaligned sequences still share a dataset with aligned sequences + */ + SequenceI dna5 = new Sequence("dna5", "CCCgggtttAAA"); + dna5.createDatasetSequence(); + al2.addSequence(dna5); + assertTrue(AlignmentUtils.alignAsSameSequences(al1, al2)); + assertEquals(seq1, al1.getSequenceAt(0).getSequenceAsString()); + assertEquals(seq2, al1.getSequenceAt(1).getSequenceAsString()); + + /* + * add another sequence to 'unaligned' - should fail, since now not + * all unaligned sequences share a dataset with aligned sequences + */ + SequenceI dna6 = new Sequence("dna6", "CCCgggtttAAA"); + dna6.createDatasetSequence(); + al1.addSequence(dna6); + // JAL-2110 JBP Comment: what's the use case for this behaviour ? + assertFalse(AlignmentUtils.alignAsSameSequences(al1, al2)); + } + + @Test(groups = "Functional") + public void testAlignAsSameSequencesMultipleSubSeq() + { + SequenceI dna1 = new Sequence("dna1", "cccGGGTTTaaa"); + SequenceI dna2 = new Sequence("dna2", "CCCgggtttAAA"); + SequenceI as1 = dna1.deriveSequence(); + SequenceI as2 = dna1.deriveSequence().getSubSequence(3, 7); + SequenceI as3 = dna2.deriveSequence(); + as1.insertCharAt(6, 5, '-'); + String s_as1 = as1.getSequenceAsString(); + as2.insertCharAt(6, 5, '-'); + String s_as2 = as2.getSequenceAsString(); + as3.insertCharAt(6, 5, '-'); + String s_as3 = as3.getSequenceAsString(); + AlignmentI aligned = new Alignment(new SequenceI[] { as1, as2, as3 }); + + // why do we need to cast this still ? + ((Alignment) aligned).createDatasetAlignment(); + SequenceI uas1 = dna1.deriveSequence(); + SequenceI uas2 = dna1.deriveSequence().getSubSequence(3, 7); + SequenceI uas3 = dna2.deriveSequence(); + AlignmentI tobealigned = new Alignment(new SequenceI[] { uas1, uas2, + uas3 }); + ((Alignment) tobealigned).createDatasetAlignment(); + + assertTrue(AlignmentUtils.alignAsSameSequences(tobealigned, aligned)); + assertEquals(s_as1, uas1.getSequenceAsString()); + assertEquals(s_as2, uas2.getSequenceAsString()); + assertEquals(s_as3, uas3.getSequenceAsString()); + } + } diff --git a/test/jalview/analysis/CrossRefTest.java b/test/jalview/analysis/CrossRefTest.java index bbc23e5..24ddb34 100644 --- a/test/jalview/analysis/CrossRefTest.java +++ b/test/jalview/analysis/CrossRefTest.java @@ -21,10 +21,31 @@ package jalview.analysis; import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNotSame; +import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertSame; +import static org.testng.AssertJUnit.assertTrue; +import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping; +import jalview.datamodel.Alignment; +import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; +import jalview.datamodel.Mapping; +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceI; +import jalview.util.DBRefUtils; +import jalview.util.MapList; +import jalview.ws.SequenceFetcher; +import jalview.ws.SequenceFetcherFactory; +import java.util.ArrayList; +import java.util.List; + +import org.testng.annotations.AfterClass; import org.testng.annotations.Test; public class CrossRefTest @@ -40,27 +61,673 @@ public class CrossRefTest DBRefEntry ref6 = new DBRefEntry("emblCDS", "1", "A123"); DBRefEntry ref7 = new DBRefEntry("GeneDB", "1", "A123"); DBRefEntry ref8 = new DBRefEntry("PFAM", "1", "A123"); + // ENSEMBL is a source of either dna or protein sequence data + DBRefEntry ref9 = new DBRefEntry("ENSEMBL", "1", "A123"); DBRefEntry[] refs = new DBRefEntry[] { ref1, ref2, ref3, ref4, ref5, - ref6, ref7, ref8 }; + ref6, ref7, ref8, ref9 }; /* * Just the DNA refs: */ - DBRefEntry[] found = CrossRef.findXDbRefs(false, refs); - assertEquals(3, found.length); + DBRefEntry[] found = DBRefUtils.selectDbRefs(true, refs); + assertEquals(4, found.length); assertSame(ref5, found[0]); assertSame(ref6, found[1]); assertSame(ref7, found[2]); + assertSame(ref9, found[3]); /* * Just the protein refs: */ - found = CrossRef.findXDbRefs(true, refs); + found = DBRefUtils.selectDbRefs(false, refs); assertEquals(4, found.length); assertSame(ref1, found[0]); assertSame(ref2, found[1]); - assertSame(ref3, found[2]); - assertSame(ref4, found[3]); + assertSame(ref4, found[2]); + assertSame(ref9, found[3]); + } + + /** + * Test the method that finds a sequence's "product" xref source databases, + * which may be direct (dbrefs on the sequence), or indirect (dbrefs on + * sequences which share a dbref with the sequence + */ + @Test(groups = { "Functional" }, enabled = true) + public void testFindXrefSourcesForSequence_proteinToDna() + { + SequenceI seq = new Sequence("Seq1", "MGKYQARLSS"); + List sources = new ArrayList(); + AlignmentI al = new Alignment(new SequenceI[] {}); + + /* + * first with no dbrefs to search + */ + sources = new CrossRef(new SequenceI[] { seq }, al) + .findXrefSourcesForSequences(false); + assertTrue(sources.isEmpty()); + + /* + * add some dbrefs to sequence + */ + // protein db is not a candidate for findXrefSources + seq.addDBRef(new DBRefEntry("UNIPROT", "0", "A1234")); + // dna coding databatases are + seq.addDBRef(new DBRefEntry("EMBL", "0", "E2345")); + // a second EMBL xref should not result in a duplicate + seq.addDBRef(new DBRefEntry("EMBL", "0", "E2346")); + seq.addDBRef(new DBRefEntry("EMBLCDS", "0", "E2347")); + seq.addDBRef(new DBRefEntry("GENEDB", "0", "E2348")); + seq.addDBRef(new DBRefEntry("ENSEMBL", "0", "E2349")); + seq.addDBRef(new DBRefEntry("ENSEMBLGENOMES", "0", "E2350")); + sources = new CrossRef(new SequenceI[] { seq }, al) + .findXrefSourcesForSequences(false); + assertEquals(4, sources.size()); + assertEquals("[EMBL, EMBLCDS, GENEDB, ENSEMBL]", sources.toString()); + + /* + * add a sequence to the alignment which has a dbref to UNIPROT|A1234 + * and others to dna coding databases + */ + sources.clear(); + seq.setDBRefs(null); + seq.addDBRef(new DBRefEntry("UNIPROT", "0", "A1234")); + seq.addDBRef(new DBRefEntry("EMBLCDS", "0", "E2347")); + SequenceI seq2 = new Sequence("Seq2", "MGKYQARLSS"); + seq2.addDBRef(new DBRefEntry("UNIPROT", "0", "A1234")); + seq2.addDBRef(new DBRefEntry("EMBL", "0", "E2345")); + seq2.addDBRef(new DBRefEntry("GENEDB", "0", "E2348")); + // TODO include ENSEMBLGENOMES in DBRefSource.DNACODINGDBS ? + al.addSequence(seq2); + sources = new CrossRef(new SequenceI[] { seq, seq2 }, al) + .findXrefSourcesForSequences(false); + assertEquals(3, sources.size()); + assertEquals("[EMBLCDS, EMBL, GENEDB]", sources.toString()); + } + + /** + * Test for finding 'product' sequences for the case where only an indirect + * xref is found - not on the nucleotide sequence but on a peptide sequence in + * the alignment which which it shares a nucleotide dbref + */ + @Test(groups = { "Functional" }, enabled = true) + public void testFindXrefSequences_indirectDbrefToProtein() + { + /* + * Alignment setup: + * - nucleotide dbref EMBL|AF039662 + * - peptide dbrefs EMBL|AF039662, UNIPROT|Q9ZTS2 + */ + SequenceI emblSeq = new Sequence("AF039662", "GGGGCAGCACAAGAAC"); + emblSeq.addDBRef(new DBRefEntry("EMBL", "0", "AF039662")); + SequenceI uniprotSeq = new Sequence("Q9ZTS2", "MASVSATMISTS"); + uniprotSeq.addDBRef(new DBRefEntry("EMBL", "0", "AF039662")); + uniprotSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2")); + + /* + * Find UNIPROT xrefs for nucleotide + * - it has no UNIPROT dbref of its own + * - but peptide with matching nucleotide dbref does, so is returned + */ + AlignmentI al = new Alignment(new SequenceI[] { emblSeq, uniprotSeq }); + Alignment xrefs = new CrossRef(new SequenceI[] { emblSeq }, al) + .findXrefSequences("UNIPROT", true); + assertEquals(1, xrefs.getHeight()); + assertSame(uniprotSeq, xrefs.getSequenceAt(0)); + } + + /** + * Test for finding 'product' sequences for the case where only an indirect + * xref is found - not on the peptide sequence but on a nucleotide sequence in + * the alignment which which it shares a protein dbref + */ + @Test(groups = { "Functional" }, enabled = true) + public void testFindXrefSequences_indirectDbrefToNucleotide() + { + /* + * Alignment setup: + * - peptide dbref UNIPROT|Q9ZTS2 + * - nucleotide dbref EMBL|AF039662, UNIPROT|Q9ZTS2 + */ + SequenceI uniprotSeq = new Sequence("Q9ZTS2", "MASVSATMISTS"); + uniprotSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2")); + SequenceI emblSeq = new Sequence("AF039662", "GGGGCAGCACAAGAAC"); + emblSeq.addDBRef(new DBRefEntry("EMBL", "0", "AF039662")); + emblSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2")); + + /* + * find EMBL xrefs for peptide sequence - it has no direct + * dbrefs, but the 'corresponding' nucleotide sequence does, so is returned + */ + /* + * Find EMBL xrefs for peptide + * - it has no EMBL dbref of its own + * - but nucleotide with matching peptide dbref does, so is returned + */ + AlignmentI al = new Alignment(new SequenceI[] { emblSeq, uniprotSeq }); + Alignment xrefs = new CrossRef(new SequenceI[] { uniprotSeq }, al) + .findXrefSequences("EMBL", false); + assertEquals(1, xrefs.getHeight()); + assertSame(emblSeq, xrefs.getSequenceAt(0)); + } + + /** + * Test for finding 'product' sequences for the case where the selected + * sequence has no dbref to the desired source, and there are no indirect + * references via another sequence in the alignment + */ + @Test(groups = { "Functional" }) + public void testFindXrefSequences_noDbrefs() + { + /* + * two nucleotide sequences, one with UNIPROT dbref + */ + SequenceI dna1 = new Sequence("AF039662", "GGGGCAGCACAAGAAC"); + dna1.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2")); + SequenceI dna2 = new Sequence("AJ307031", "AAACCCTTT"); + + /* + * find UNIPROT xrefs for peptide sequence - it has no direct + * dbrefs, and the other sequence (which has a UNIPROT dbref) is not + * equatable to it, so no results found + */ + AlignmentI al = new Alignment(new SequenceI[] { dna1, dna2 }); + Alignment xrefs = new CrossRef(new SequenceI[] { dna2 }, al) + .findXrefSequences("UNIPROT", true); + assertNull(xrefs); + } + + /** + * Tests for the method that searches an alignment (with one sequence + * excluded) for protein/nucleotide sequences with a given cross-reference + */ + @Test(groups = { "Functional" }, enabled = true) + public void testSearchDataset() + { + /* + * nucleotide sequence with UNIPROT AND EMBL dbref + * peptide sequence with UNIPROT dbref + */ + SequenceI dna1 = new Sequence("AF039662", "GGGGCAGCACAAGAAC"); + Mapping map = new Mapping(new Sequence("pep2", "MLAVSRG"), new MapList( + new int[] { 1, 21 }, new int[] { + 1, 7 }, 3, 1)); + DBRefEntry dbref = new DBRefEntry("UNIPROT", "0", "Q9ZTS2", map); + dna1.addDBRef(dbref); + dna1.addDBRef(new DBRefEntry("EMBL", "0", "AF039662")); + SequenceI pep1 = new Sequence("Q9ZTS2", "MLAVSRGQ"); + dbref = new DBRefEntry("UNIPROT", "0", "Q9ZTS2"); + pep1.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2")); + AlignmentI al = new Alignment(new SequenceI[] { dna1, pep1 }); + + List result = new ArrayList(); + + /* + * first search for a dbref nowhere on the alignment: + */ + dbref = new DBRefEntry("UNIPROT", "0", "P30419"); + CrossRef testee = new CrossRef(al.getSequencesArray(), al); + AlignedCodonFrame acf = new AlignedCodonFrame(); + boolean found = testee.searchDataset(true, dna1, dbref, result, acf, + true); + assertFalse(found); + assertTrue(result.isEmpty()); + assertTrue(acf.isEmpty()); + + /* + * search for a protein sequence with dbref UNIPROT:Q9ZTS2 + */ + acf = new AlignedCodonFrame(); + dbref = new DBRefEntry("UNIPROT", "0", "Q9ZTS2"); + found = testee.searchDataset(!dna1.isProtein(), dna1, dbref, result, + acf, false); // search dataset with a protein xref from a dna + // sequence to locate the protein product + assertTrue(found); + assertEquals(1, result.size()); + assertSame(pep1, result.get(0)); + assertTrue(acf.isEmpty()); + + /* + * search for a nucleotide sequence with dbref UNIPROT:Q9ZTS2 + */ + result.clear(); + acf = new AlignedCodonFrame(); + dbref = new DBRefEntry("UNIPROT", "0", "Q9ZTS2"); + found = testee.searchDataset(!pep1.isProtein(), pep1, dbref, result, + acf, false); // search dataset with a protein's direct dbref to + // locate dna sequences with matching xref + assertTrue(found); + assertEquals(1, result.size()); + assertSame(dna1, result.get(0)); + // should now have a mapping from dna to pep1 + List mappings = acf.getMappings(); + assertEquals(1, mappings.size()); + SequenceToSequenceMapping mapping = mappings.get(0); + assertSame(dna1, mapping.getFromSeq()); + assertSame(pep1, mapping.getMapping().getTo()); + MapList mapList = mapping.getMapping().getMap(); + assertEquals(1, mapList.getToRatio()); + assertEquals(3, mapList.getFromRatio()); + assertEquals(1, mapList.getFromRanges().size()); + assertEquals(1, mapList.getFromRanges().get(0)[0]); + assertEquals(21, mapList.getFromRanges().get(0)[1]); + assertEquals(1, mapList.getToRanges().size()); + assertEquals(1, mapList.getToRanges().get(0)[0]); + assertEquals(7, mapList.getToRanges().get(0)[1]); + } + + /** + * Test for finding 'product' sequences for the case where the selected + * sequence has a dbref with a mapping to a sequence. This represents the case + * where either + *
            + *
          • a fetched sequence is already decorated with its cross-reference (e.g. + * EMBL + translation), or
          • + *
          • Get Cross-References has been done once resulting in instantiated + * cross-reference mappings
          • + *
          + */ + @Test(groups = { "Functional" }) + public void testFindXrefSequences_fromDbRefMap() + { + /* + * scenario: nucleotide sequence AF039662 + * with dbref + mapping to Q9ZTS2 and P30419 + * which themselves each have a dbref and feature + */ + SequenceI dna1 = new Sequence("AF039662", "GGGGCAGCACAAGAAC"); + SequenceI pep1 = new Sequence("Q9ZTS2", "MALFQRSV"); + SequenceI pep2 = new Sequence("P30419", "MTRRSQIF"); + dna1.createDatasetSequence(); + pep1.createDatasetSequence(); + pep2.createDatasetSequence(); + + pep1.getDatasetSequence().addDBRef( + new DBRefEntry("Pfam", "0", "PF00111")); + pep1.addSequenceFeature(new SequenceFeature("type", "desc", 12, 14, 1f, + "group")); + pep2.getDatasetSequence().addDBRef(new DBRefEntry("PDB", "0", "3JTK")); + pep2.addSequenceFeature(new SequenceFeature("type2", "desc2", 13, 15, + 12f, "group2")); + + MapList mapList = new MapList(new int[] { 1, 24 }, new int[] { 1, 3 }, + 3, 1); + Mapping map = new Mapping(pep1, mapList); + DBRefEntry dbRef1 = new DBRefEntry("UNIPROT", "0", "Q9ZTS2", map); + dna1.getDatasetSequence().addDBRef(dbRef1); + mapList = new MapList(new int[] { 1, 24 }, new int[] { 1, 3 }, 3, 1); + map = new Mapping(pep2, mapList); + DBRefEntry dbRef2 = new DBRefEntry("UNIPROT", "0", "P30419", map); + dna1.getDatasetSequence().addDBRef(dbRef2); + + /* + * find UNIPROT xrefs for nucleotide sequence - it should pick up + * mapped sequences + */ + AlignmentI al = new Alignment(new SequenceI[] { dna1 }); + Alignment xrefs = new CrossRef(new SequenceI[] { dna1 }, al) + .findXrefSequences("UNIPROT", true); + assertEquals(2, xrefs.getHeight()); + + /* + * cross-refs alignment holds copies of the mapped sequences + * including copies of their dbrefs and features + */ + checkCopySequence(pep1, xrefs.getSequenceAt(0)); + checkCopySequence(pep2, xrefs.getSequenceAt(1)); + } + + /** + * Helper method that verifies that 'copy' has the same name, start, end, + * sequence and dataset sequence object as 'original' (but is not the same + * object) + * + * @param copy + * @param original + */ + private void checkCopySequence(SequenceI copy, SequenceI original) + { + assertNotSame(copy, original); + assertSame(copy.getDatasetSequence(), original.getDatasetSequence()); + assertEquals(copy.getName(), original.getName()); + assertEquals(copy.getStart(), original.getStart()); + assertEquals(copy.getEnd(), original.getEnd()); + assertEquals(copy.getSequenceAsString(), original.getSequenceAsString()); + } + + /** + * Test for finding 'product' sequences for the case where the selected + * sequence has a dbref with no mapping, triggering a fetch from database + */ + @Test(groups = { "Functional" }) + public void testFindXrefSequences_withFetch() + { + SequenceI dna1 = new Sequence("AF039662", "GGGGCAGCACAAGAAC"); + dna1.addDBRef(new DBRefEntry("UNIPROT", "0", "Q9ZTS2")); + dna1.addDBRef(new DBRefEntry("UNIPROT", "0", "P30419")); + dna1.addDBRef(new DBRefEntry("UNIPROT", "0", "P00314")); + final SequenceI pep1 = new Sequence("Q9ZTS2", "MYQLIRSSW"); + final SequenceI pep2 = new Sequence("P00314", "MRKLLAASG"); + + /* + * argument false suppresses adding DAS sources + * todo: define an interface type SequenceFetcherI and mock that + */ + SequenceFetcher mockFetcher = new SequenceFetcher(false) + { + @Override + public boolean isFetchable(String source) + { + return true; + } + + @Override + public SequenceI[] getSequences(List refs, boolean dna) + { + return new SequenceI[] { pep1, pep2 }; + } + }; + SequenceFetcherFactory.setSequenceFetcher(mockFetcher); + + /* + * find UNIPROT xrefs for nucleotide sequence + */ + AlignmentI al = new Alignment(new SequenceI[] { dna1 }); + Alignment xrefs = new CrossRef(new SequenceI[] { dna1 }, al) + .findXrefSequences("UNIPROT", true); + assertEquals(2, xrefs.getHeight()); + assertSame(pep1, xrefs.getSequenceAt(0)); + assertSame(pep2, xrefs.getSequenceAt(1)); + } + + @AfterClass + public void tearDown() + { + SequenceFetcherFactory.setSequenceFetcher(null); + } + + /** + * Test for finding 'product' sequences for the case where both gene and + * transcript sequences have dbrefs to Uniprot. + */ + @Test(groups = { "Functional" }) + public void testFindXrefSequences_forGeneAndTranscripts() + { + /* + * 'gene' sequence + */ + SequenceI gene = new Sequence("ENSG00000157764", "CGCCTCCCTTCCCC"); + gene.addDBRef(new DBRefEntry("UNIPROT", "0", "P15056")); + gene.addDBRef(new DBRefEntry("UNIPROT", "0", "H7C5K3")); + + /* + * 'transcript' with CDS feature (supports mapping to protein) + */ + SequenceI braf001 = new Sequence("ENST00000288602", "taagATGGCGGCGCTGa"); + braf001.addDBRef(new DBRefEntry("UNIPROT", "0", "P15056")); + braf001.addSequenceFeature(new SequenceFeature("CDS", "", 5, 16, 0f, + null)); + + /* + * 'spliced transcript' with CDS ranges + */ + SequenceI braf002 = new Sequence("ENST00000497784", "gCAGGCtaTCTGTTCaa"); + braf002.addDBRef(new DBRefEntry("UNIPROT", "0", "H7C5K3")); + braf002.addSequenceFeature(new SequenceFeature("CDS", "", 2, 6, 0f, + null)); + braf002.addSequenceFeature(new SequenceFeature("CDS", "", 9, 15, 0f, + null)); + + /* + * TODO code is fragile - use of SequenceIdMatcher depends on fetched + * sequences having a name starting Source|Accession + * which happens to be true for Uniprot,PDB,EMBL but not Pfam,Rfam,Ensembl + */ + final SequenceI pep1 = new Sequence("UNIPROT|P15056", "MAAL"); + final SequenceI pep2 = new Sequence("UNIPROT|H7C5K3", "QALF"); + + /* + * argument false suppresses adding DAS sources + * todo: define an interface type SequenceFetcherI and mock that + */ + SequenceFetcher mockFetcher = new SequenceFetcher(false) + { + @Override + public boolean isFetchable(String source) + { + return true; + } + + @Override + public SequenceI[] getSequences(List refs, boolean dna) + { + return new SequenceI[] { pep1, pep2 }; + } + }; + SequenceFetcherFactory.setSequenceFetcher(mockFetcher); + + /* + * find UNIPROT xrefs for gene and transcripts + * verify that + * - the two proteins are retrieved but not duplicated + * - mappings are built from transcript (CDS) to proteins + * - no mappings from gene to proteins + */ + SequenceI[] seqs = new SequenceI[] { gene, braf001, braf002 }; + AlignmentI al = new Alignment(seqs); + Alignment xrefs = new CrossRef(seqs, al).findXrefSequences("UNIPROT", + true); + assertEquals(2, xrefs.getHeight()); + assertSame(pep1, xrefs.getSequenceAt(0)); + assertSame(pep2, xrefs.getSequenceAt(1)); + } + + /** + *
          +   * Test that emulates this (real but simplified) case:
          +   * Alignment:          DBrefs
          +   *     UNIPROT|P0CE19  EMBL|J03321, EMBL|X06707, EMBL|M19487
          +   *     UNIPROT|P0CE20  EMBL|J03321, EMBL|X06707, EMBL|X07547
          +   * Find cross-references for EMBL. These are mocked here as
          +   *     EMBL|J03321     with mappings to P0CE18, P0CE19, P0CE20
          +   *     EMBL|X06707     with mappings to P0CE17, P0CE19, P0CE20
          +   *     EMBL|M19487     with mappings to P0CE19, Q46432
          +   *     EMBL|X07547     with mappings to P0CE20, B0BCM4
          +   * EMBL sequences are first 'fetched' (mocked here) for P0CE19.
          +   * The 3 EMBL sequences are added to the alignment dataset.
          +   * Their dbrefs to Uniprot products P0CE19 and P0CE20 should be matched in the
          +   * alignment dataset and updated to reference the original Uniprot sequences.
          +   * For the second Uniprot sequence, the J03321 and X06707 xrefs should be 
          +   * resolved from the dataset, and only the X07547 dbref fetched.
          +   * So the end state to verify is:
          +   * - 4 cross-ref sequences returned: J03321, X06707,  M19487, X07547
          +   * - P0CE19/20 dbrefs to EMBL sequences now have mappings
          +   * - J03321 dbrefs to P0CE19/20 mapped to original Uniprot sequences
          +   * - X06707 dbrefs to P0CE19/20 mapped to original Uniprot sequences
          +   * 
          + */ + @Test(groups = { "Functional" }) + public void testFindXrefSequences_uniprotEmblManyToMany() + { + /* + * Uniprot sequences, both with xrefs to EMBL|J03321 + * and EMBL|X07547 + */ + SequenceI p0ce19 = new Sequence("UNIPROT|P0CE19", "KPFG"); + p0ce19.addDBRef(new DBRefEntry("EMBL", "0", "J03321")); + p0ce19.addDBRef(new DBRefEntry("EMBL", "0", "X06707")); + p0ce19.addDBRef(new DBRefEntry("EMBL", "0", "M19487")); + SequenceI p0ce20 = new Sequence("UNIPROT|P0CE20", "PFGK"); + p0ce20.addDBRef(new DBRefEntry("EMBL", "0", "J03321")); + p0ce20.addDBRef(new DBRefEntry("EMBL", "0", "X06707")); + p0ce20.addDBRef(new DBRefEntry("EMBL", "0", "X07547")); + + /* + * EMBL sequences to be 'fetched', complete with dbrefs and mappings + * to their protein products (CDS location and translations are provided + * in EMBL XML); these should be matched to, and replaced with, + * the corresponding uniprot sequences after fetching + */ + + /* + * J03321 with mappings to P0CE19 and P0CE20 + */ + final SequenceI j03321 = new Sequence("EMBL|J03321", "AAACCCTTTGGGAAAA"); + DBRefEntry dbref1 = new DBRefEntry("UNIPROT", "0", "P0CE19"); + MapList mapList = new MapList(new int[] { 1, 12 }, new int[] { 1, 4 }, + 3, 1); + Mapping map = new Mapping(new Sequence("UNIPROT|P0CE19", "KPFG"), + mapList); + // add a dbref to the mapped to sequence - should get copied to p0ce19 + map.getTo().addDBRef(new DBRefEntry("PIR", "0", "S01875")); + dbref1.setMap(map); + j03321.addDBRef(dbref1); + DBRefEntry dbref2 = new DBRefEntry("UNIPROT", "0", "P0CE20"); + mapList = new MapList(new int[] { 4, 15 }, new int[] { 2, 5 }, 3, 1); + dbref2.setMap(new Mapping(new Sequence("UNIPROT|P0CE20", "PFGK"), + new MapList(mapList))); + j03321.addDBRef(dbref2); + + /* + * X06707 with mappings to P0CE19 and P0CE20 + */ + final SequenceI x06707 = new Sequence("EMBL|X06707", "atgAAACCCTTTGGG"); + DBRefEntry dbref3 = new DBRefEntry("UNIPROT", "0", "P0CE19"); + MapList map2 = new MapList(new int[] { 4, 15 }, new int[] { 1, 4 }, 3, + 1); + dbref3.setMap(new Mapping(new Sequence("UNIPROT|P0CE19", "KPFG"), map2)); + x06707.addDBRef(dbref3); + DBRefEntry dbref4 = new DBRefEntry("UNIPROT", "0", "P0CE20"); + MapList map3 = new MapList(new int[] { 4, 15 }, new int[] { 1, 4 }, 3, + 1); + dbref4.setMap(new Mapping(new Sequence("UNIPROT|P0CE20", "PFGK"), map3)); + x06707.addDBRef(dbref4); + + /* + * M19487 with mapping to P0CE19 and Q46432 + */ + final SequenceI m19487 = new Sequence("EMBL|M19487", "AAACCCTTTGGG"); + DBRefEntry dbref5 = new DBRefEntry("UNIPROT", "0", "P0CE19"); + dbref5.setMap(new Mapping(new Sequence("UNIPROT|P0CE19", "KPFG"), + new MapList(mapList))); + m19487.addDBRef(dbref5); + DBRefEntry dbref6 = new DBRefEntry("UNIPROT", "0", "Q46432"); + dbref6.setMap(new Mapping(new Sequence("UNIPROT|Q46432", "KPFG"), + new MapList(mapList))); + m19487.addDBRef(dbref6); + + /* + * X07547 with mapping to P0CE20 and B0BCM4 + */ + final SequenceI x07547 = new Sequence("EMBL|X07547", "cccAAACCCTTTGGG"); + DBRefEntry dbref7 = new DBRefEntry("UNIPROT", "0", "P0CE20"); + dbref7.setMap(new Mapping(new Sequence("UNIPROT|P0CE19", "KPFG"), + new MapList(map2))); + x07547.addDBRef(dbref7); + DBRefEntry dbref8 = new DBRefEntry("UNIPROT", "0", "B0BCM4"); + dbref8.setMap(new Mapping(new Sequence("UNIPROT|B0BCM4", "KPFG"), + new MapList(map2))); + x07547.addDBRef(dbref8); + + /* + * mock sequence fetcher to 'return' the EMBL sequences + * TODO: Mockito would allow .thenReturn().thenReturn() here, + * and also capture and verification of the parameters + * passed in calls to getSequences() - important to verify that + * duplicate sequence fetches are not requested + */ + SequenceFetcher mockFetcher = new SequenceFetcher(false) + { + int call = 0; + + @Override + public boolean isFetchable(String source) + { + return true; + } + + @Override + public SequenceI[] getSequences(List refs, boolean dna) + { + call++; + if (call == 1) + { + assertEquals("Expected 3 embl seqs in first fetch", 3, + refs.size()); + return new SequenceI[] { j03321, x06707, m19487 }; + } + else + { + assertEquals("Expected 1 embl seq in second fetch", 1, + refs.size()); + return new SequenceI[] { x07547 }; + } + } + }; + SequenceFetcherFactory.setSequenceFetcher(mockFetcher); + + /* + * find EMBL xrefs for Uniprot seqs and verify that + * - the EMBL xref'd sequences are retrieved without duplicates + * - mappings are added to the Uniprot dbrefs + * - mappings in the EMBL-to-Uniprot dbrefs are updated to the + * alignment sequences + * - dbrefs on the EMBL sequences are added to the original dbrefs + */ + SequenceI[] seqs = new SequenceI[] { p0ce19, p0ce20 }; + AlignmentI al = new Alignment(seqs); + Alignment xrefs = new CrossRef(seqs, al).findXrefSequences("EMBL", + false); + + /* + * verify retrieved sequences + */ + assertNotNull(xrefs); + assertEquals(4, xrefs.getHeight()); + assertSame(j03321, xrefs.getSequenceAt(0)); + assertSame(x06707, xrefs.getSequenceAt(1)); + assertSame(m19487, xrefs.getSequenceAt(2)); + assertSame(x07547, xrefs.getSequenceAt(3)); + + /* + * verify mappings added to Uniprot-to-EMBL dbrefs + */ + Mapping mapping = p0ce19.getDBRefs()[0].getMap(); + assertSame(j03321, mapping.getTo()); + mapping = p0ce19.getDBRefs()[1].getMap(); + assertSame(x06707, mapping.getTo()); + mapping = p0ce20.getDBRefs()[0].getMap(); + assertSame(j03321, mapping.getTo()); + mapping = p0ce20.getDBRefs()[1].getMap(); + assertSame(x06707, mapping.getTo()); + + /* + * verify dbrefs on EMBL are mapped to alignment seqs + */ + assertSame(p0ce19, j03321.getDBRefs()[0].getMap().getTo()); + assertSame(p0ce20, j03321.getDBRefs()[1].getMap().getTo()); + assertSame(p0ce19, x06707.getDBRefs()[0].getMap().getTo()); + assertSame(p0ce20, x06707.getDBRefs()[1].getMap().getTo()); + + /* + * verify new dbref on EMBL dbref mapping is copied to the + * original Uniprot sequence + */ + assertEquals(4, p0ce19.getDBRefs().length); + assertEquals("PIR", p0ce19.getDBRefs()[3].getSource()); + assertEquals("S01875", p0ce19.getDBRefs()[3].getAccessionId()); } + @Test(groups = "Functional") + public void testSameSequence() + { + assertTrue(CrossRef.sameSequence(null, null)); + SequenceI seq1 = new Sequence("seq1", "ABCDEF"); + assertFalse(CrossRef.sameSequence(seq1, null)); + assertFalse(CrossRef.sameSequence(null, seq1)); + assertTrue(CrossRef.sameSequence(seq1, new Sequence("seq2", "ABCDEF"))); + assertTrue(CrossRef.sameSequence(seq1, new Sequence("seq2", "abcdef"))); + assertFalse(CrossRef + .sameSequence(seq1, new Sequence("seq2", "ABCDE-F"))); + assertFalse(CrossRef.sameSequence(seq1, new Sequence("seq2", "BCDEF"))); + } } diff --git a/test/jalview/analysis/DnaTest.java b/test/jalview/analysis/DnaTest.java index 9a4c357..0142ab5 100644 --- a/test/jalview/analysis/DnaTest.java +++ b/test/jalview/analysis/DnaTest.java @@ -29,6 +29,7 @@ import jalview.datamodel.AlignedCodon; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; +import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.gui.AlignViewport; import jalview.io.FormatAdapter; @@ -498,17 +499,44 @@ public class DnaTest @Test(groups = "Functional") public void testReverseSequence() { - String seq = "AcGtUrYkMbVdHNX"; + String seq = "-Ac-GtU--rYkMbVdHNX-"; + String seqRev = new StringBuilder(seq).reverse().toString(); // reverse: SequenceI reversed = Dna.reverseSequence("Seq1", seq, false); - assertEquals(new StringBuilder(seq).reverse() - .toString(), reversed.getSequenceAsString()); + assertEquals(1, reversed.getStart()); + assertEquals(15, reversed.getEnd()); + assertEquals(20, reversed.getLength()); + assertEquals(seqRev, reversed.getSequenceAsString()); assertEquals("Seq1|rev", reversed.getName()); // reverse complement: SequenceI revcomp = Dna.reverseSequence("Seq1", seq, true); - assertEquals("XNDhBvKmRyAaCgT", revcomp.getSequenceAsString()); + assertEquals("-XNDhBvKmRy--AaC-gT-", revcomp.getSequenceAsString()); assertEquals("Seq1|revcomp", revcomp.getName()); } + + @Test(groups = "Functional") + public void testReverseCdna() + { + String seq = "-Ac-GtU--rYkMbVdHNX-"; + String seqRev = new StringBuilder(seq).reverse().toString(); + String seqDs = seq.replaceAll("-", ""); + String seqDsRev = new StringBuilder(seqDs).reverse().toString(); + + SequenceI dna = new Sequence("Seq1", seq); + Alignment al = new Alignment(new SequenceI[] {dna}); + al.createDatasetAlignment(); + assertEquals(seqDs, al.getSequenceAt(0).getDatasetSequence() + .getSequenceAsString()); + + ColumnSelection cs = new ColumnSelection(); + AlignViewportI av = new AlignViewport(al, cs); + Dna testee = new Dna(av, new int[] { 0, al.getWidth() - 1 }); + AlignmentI reversed = testee.reverseCdna(false); + assertEquals(1, reversed.getHeight()); + assertEquals(seqRev, reversed.getSequenceAt(0).getSequenceAsString()); + assertEquals(seqDsRev, reversed.getSequenceAt(0).getDatasetSequence() + .getSequenceAsString()); + } } diff --git a/test/jalview/analysis/GroupingTest.java b/test/jalview/analysis/GroupingTest.java index 55823e4..df39b81 100644 --- a/test/jalview/analysis/GroupingTest.java +++ b/test/jalview/analysis/GroupingTest.java @@ -27,7 +27,6 @@ import jalview.datamodel.Sequence; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; -import java.util.ArrayList; import java.util.Arrays; import org.testng.AssertJUnit; @@ -45,21 +44,26 @@ public class GroupingTest Sequence s5 = new Sequence("s5", "AAAADDEDTTEE"); - SequenceGroup sg1 = new SequenceGroup(Arrays.asList(new SequenceI[] { s1, + SequenceGroup sg_12 = new SequenceGroup(Arrays.asList(new SequenceI[] { s1, s2 }), "Group1", null, false, false, false, 0, 5); - SequenceGroup sg2 = new SequenceGroup(Arrays.asList(new SequenceI[] { s3, + SequenceGroup sg_345 = new SequenceGroup(Arrays.asList(new SequenceI[] { s3, s4, s5 }), "Group2", null, false, false, false, 0, 5); AlignmentI alignment = new Alignment( new SequenceI[] { s1, s2, s3, s4, s5 }); - int[] positions = new int[] { 1, 7, 9 }; + /* + * test for the case where column selections are not added in + * left to right order + */ + int[] positions = new int[] { 7, 9, 1 }; @Test(groups = { "Functional" }) public void testMakeGroupsWithBoth() { - ArrayList str = new ArrayList(); + String[] str = new String[alignment.getHeight()]; + int seq = 0; for (SequenceI s : alignment.getSequences()) { StringBuilder sb = new StringBuilder(); @@ -67,12 +71,12 @@ public class GroupingTest { sb.append(s.getCharAt(p)); } - str.add(sb.toString()); + str[seq++] = sb.toString(); } SequenceGroup[] seqgroupsString = Grouping.makeGroupsFrom( - alignment.getSequencesArray(), - str.toArray(new String[str.size()]), - Arrays.asList(new SequenceGroup[] { sg1, sg2 })); + alignment.getSequencesArray(), str, + Arrays.asList(new SequenceGroup[] { sg_12, sg_345 })); + ColumnSelection cs = new ColumnSelection(); for (int p : positions) { @@ -80,7 +84,7 @@ public class GroupingTest } SequenceGroup[] seqgroupsColSel = Grouping.makeGroupsFromCols( alignment.getSequencesArray(), cs, - Arrays.asList(new SequenceGroup[] { sg1, sg2 })); + Arrays.asList(new SequenceGroup[] { sg_12, sg_345 })); AssertJUnit .assertEquals(seqgroupsString.length, seqgroupsColSel.length); for (int p = 0; p < seqgroupsString.length; p++) diff --git a/test/jalview/analysis/RnaTest.java b/test/jalview/analysis/RnaTest.java index 5801437..f33525f 100644 --- a/test/jalview/analysis/RnaTest.java +++ b/test/jalview/analysis/RnaTest.java @@ -21,6 +21,9 @@ package jalview.analysis; import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; import jalview.analysis.SecStrConsensus.SimpleBP; @@ -35,11 +38,12 @@ public class RnaTest public void testGetSimpleBPs() throws WUSSParseException { String rna = "([{})]"; // JAL-1081 example - Vector bps = Rna.GetSimpleBPs(rna); + Vector bps = Rna.getSimpleBPs(rna); assertEquals(3, bps.size()); /* * the base pairs are added in the order in which the matching base is found + * (popping the stack of unmatched opening brackets) */ assertEquals(2, bps.get(0).bp5); // { assertEquals(3, bps.get(0).bp3); // } @@ -55,25 +59,221 @@ public class RnaTest String rna = "(([{})]"; try { - Rna.GetSimpleBPs(rna); + Rna.getSimpleBPs(rna); fail("expected exception"); } catch (WUSSParseException e) { - // expected + // error reported as after end of input string + assertEquals(rna.length(), e.getProblemPos()); } } @Test(groups = { "Functional" }) public void testGetSimpleBPs_unmatchedCloser() { - String rna = "([{})]]"; + String rna = "([{})]]]"; try { - Rna.GetSimpleBPs(rna); + Rna.getSimpleBPs(rna); fail("expected exception"); } catch (WUSSParseException e) { - // expected + // error reported as at first unmatched close + assertEquals(6, e.getProblemPos()); + } + + /* + * a variant where we have no opening bracket of the same type + * as the unmatched closing bracket (no stack rather than empty stack) + */ + rna = "((()])"; + try + { + Rna.getSimpleBPs(rna); + fail("expected exception"); + } catch (WUSSParseException e) + { + assertEquals(4, e.getProblemPos()); + } + } + + @Test(groups = { "Functional" }) + public void testGetRNASecStrucState() + { + assertNull(Rna.getRNASecStrucState(null)); + for (int i = 0; i <= 255; i++) + { + String s = String.valueOf((char) i); + String ss = Rna.getRNASecStrucState(s); + + /* + * valid SS chars are a-z, A-Z, and various brackets; + * anything else is returned as a space + */ + if ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') + || "()[]{}<>".indexOf(s) > -1) + { + assertEquals("" + i, s, ss); + } + else + { + assertEquals(" ", ss); + } + } + + /* + * a string is processed character by character + */ + assertEquals("a [K ]z} {Q b(w)p> are closing bracket symbols + */ + for (int i = 0; i <= 255; i++) + { + boolean isClosingChar = Rna.isClosingParenthesis((char) i); + boolean isClosingString = Rna.isClosingParenthesis(String + .valueOf((char) i)); + if ((i >= 'a' && i <= 'z') || i == ')' || i == '}' || i == ']' + || i == '>') + { + assertTrue(String.format("close base pair %c", i), isClosingChar); + assertTrue(String.format("close base pair %c", i), isClosingString); + } + else + { + assertFalse(String.format("close base pair %c", i), isClosingChar); + assertFalse(String.format("close base pair %c", i), isClosingString); + } + assertFalse(Rna.isClosingParenthesis(String.valueOf((char) i) + " ")); + } + } + + @Test(groups = { "Functional" }) + public void testIsCanonicalOrWobblePair() + { + String bases = "acgtuACGTU"; + for (int i = 0; i < bases.length(); i++) + { + for (int j = 0; j < bases.length(); j++) + { + char first = bases.charAt(i); + char second = bases.charAt(j); + boolean result = Rna.isCanonicalOrWobblePair(first, second); + String pair = new String(new char[] { first, second }) + .toUpperCase(); + if (pair.equals("AT") || pair.equals("TA") || pair.equals("AU") + || pair.equals("UA") || pair.equals("GC") + || pair.equals("CG") || pair.equals("GT") + || pair.equals("TG") || pair.equals("GU") + || pair.equals("UG")) + { + assertTrue(pair + " should be valid", result); + } + else + { + assertFalse(pair + " should be invalid", result); + } + } + } + } + + /** + * Tests for isOpeningParenthesis with char or String argument + */ + @Test(groups = { "Functional" }) + public void testIsOpeningParenthesis() + { + /* + * only A-Z, ([{< are opening bracket symbols + */ + for (int i = 0; i <= 255; i++) + { + boolean isOpeningChar = Rna.isOpeningParenthesis((char) i); + boolean isOpeningString = Rna.isOpeningParenthesis(String + .valueOf((char) i)); + if ((i >= 'A' && i <= 'Z') || i == '(' || i == '{' || i == '[' + || i == '<') + { + assertTrue(String.format("Open base pair %c", i), isOpeningChar); + assertTrue(String.format("Open base pair %c", i), isOpeningString); + } + else + { + assertFalse(String.format("Open base pair %c", i), isOpeningChar); + assertFalse(String.format("Open base pair %c", i), isOpeningString); + } + assertFalse(Rna.isOpeningParenthesis(String.valueOf((char) i) + " ")); + } + } + + @Test(groups = { "Functional" }) + public void testGetMatchingOpeningParenthesis() throws WUSSParseException + { + for (int i = 0; i <= 255; i++) + { + boolean isClosing = Rna.isClosingParenthesis((char) i); + if (isClosing) + { + char opening = Rna.getMatchingOpeningParenthesis((char) i); + if (i >= 'a' && i <= 'z') + { + assertEquals(i + 'A' - 'a', opening); + } + else if (i == ')' && opening == '(' || i == ']' && opening == '[' + || i == '}' && opening == '{' || i == '>' && opening == '<') + { + // ok + } + else + { + fail("Got " + opening + " as opening bracket pair for " + + ((char) i)); + } + } + } + } + + /** + * Tests for isRnaSecondaryStructureSymbol with char or String argument + */ + @Test(groups = { "Functional" }) + public void testIsRnaSecondaryStructureSymbol() + { + assertFalse(Rna.isRnaSecondaryStructureSymbol(null)); + + /* + * only A-Z, a-z, ()[]{}<> are valid symbols + */ + for (int i = 0; i <= 255; i++) + { + boolean isValidChar = Rna.isRnaSecondaryStructureSymbol((char) i); + boolean isValidString = Rna.isRnaSecondaryStructureSymbol(String + .valueOf((char) i)); + if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || i == '(' + || i == ')' || i == '{' || i == '}' || i == '[' || i == ']' + || i == '<' || i == '>') + { + assertTrue(String.format("close base pair %c", i), isValidChar); + assertTrue(String.format("close base pair %c", i), isValidString); + } + else + { + assertFalse(String.format("close base pair %c", i), isValidChar); + assertFalse(String.format("close base pair %c", i), isValidString); + } + assertFalse(Rna.isRnaSecondaryStructureSymbol(String + .valueOf((char) i) + " ")); } } } diff --git a/test/jalview/analysis/SequenceIdMatcherTest.java b/test/jalview/analysis/SequenceIdMatcherTest.java index eb0fc47..a17270d 100644 --- a/test/jalview/analysis/SequenceIdMatcherTest.java +++ b/test/jalview/analysis/SequenceIdMatcherTest.java @@ -50,6 +50,10 @@ public class SequenceIdMatcherTest assertTrue(testee.equals("A12345,")); assertTrue(testee.equals("A12345?")); assertTrue(testee.equals("A12345_")); + /* + * case insensitive matching + */ + assertTrue(testee.equals("a12345")); /* * matcher name = target name + word separator... @@ -58,13 +62,21 @@ public class SequenceIdMatcherTest assertTrue(testee.equals("A12345")); /* + * case insensitive matching + */ + assertTrue(testee.equals("a12345")); + + /* * miscellaneous failing cases */ testee = sequenceIdMatcher.new SeqIdName("A12345"); assertFalse(testee.equals((Object) null)); assertFalse(testee.equals("")); - assertFalse(testee.equals("a12345")); assertFalse(testee.equals("A12346|A12345")); + /* + * case insensitive matching + */ + assertTrue(testee.equals("a12345")); testee = sequenceIdMatcher.new SeqIdName("A12345?B23456"); assertFalse(testee.equals("B23456")); @@ -74,5 +86,15 @@ public class SequenceIdMatcherTest testee = sequenceIdMatcher.new SeqIdName("A12345<"); assertFalse(testee.equals("A12345?")); assertTrue(testee.equals("A12345<")); // bug? inconsistent + /* + * case insensitive matching + */ + assertTrue(testee.equals("a12345")); + + testee = sequenceIdMatcher.new SeqIdName("UNIPROT|A12345"); + assertFalse(testee.equals("A12345")); + assertFalse(testee.equals("UNIPROT|B98765")); + assertFalse(testee.equals("UNIPROT|")); + assertTrue(testee.equals("UNIPROT")); } } diff --git a/test/jalview/bin/ArgsParserTest.java b/test/jalview/bin/ArgsParserTest.java new file mode 100644 index 0000000..06e79de --- /dev/null +++ b/test/jalview/bin/ArgsParserTest.java @@ -0,0 +1,65 @@ +package jalview.bin; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; + +import org.testng.annotations.Test; + +public class ArgsParserTest +{ + @Test(groups = "Functional") + public void testGetValue() + { + ArgsParser ap = new ArgsParser(new String[] { "-name", "Henry", "-job", + "Tester" }); + assertEquals(4, ap.getSize()); + assertNull(ap.getValue("rubbish")); + assertEquals("Tester", ap.getValue("job")); + // call to getValue removes the argument and its value + assertEquals(2, ap.getSize()); + assertNull(ap.getValue("job")); + assertFalse(ap.contains("job")); + assertFalse(ap.contains("Tester")); + + assertEquals("Henry", ap.getValue("name")); + assertEquals(0, ap.getSize()); + assertNull(ap.getValue("name")); + } + + @Test(groups = "Functional") + public void testGetValue_decoded() + { + ArgsParser ap = new ArgsParser(new String[] { "-name%241", "Henry", + "-job", "Test%203%2a" }); + // parameter value is decoded + assertEquals("Test 3*", ap.getValue("job", true)); + // parameter name is not decoded + assertNull(ap.getValue("name$1", true)); + assertEquals("Henry", ap.getValue("name%241", true)); + } + + @Test(groups = "Functional") + public void testNextValue() + { + ArgsParser ap = new ArgsParser(new String[] { "-name", "Henry", "-job", + "Tester" }); + assertEquals("name", ap.nextValue()); + assertEquals("Henry", ap.nextValue()); + assertEquals("job", ap.nextValue()); + assertEquals("Tester", ap.nextValue()); + } + + @Test(groups = "Functional") + public void testContains() + { + ArgsParser ap = new ArgsParser(new String[] { "-name", "Henry", "-job", + "Tester" }); + assertFalse(ap.contains("Susan")); + assertFalse(ap.contains("-name")); + assertTrue(ap.contains("name")); + // testing for contains removes the argument + assertFalse(ap.contains("name")); + } +} diff --git a/test/jalview/bin/CacheTest.java b/test/jalview/bin/CacheTest.java new file mode 100644 index 0000000..a37370f --- /dev/null +++ b/test/jalview/bin/CacheTest.java @@ -0,0 +1,48 @@ +package jalview.bin; + +import static org.testng.AssertJUnit.assertEquals; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class CacheTest +{ + private Locale locale; + + @BeforeClass(alwaysRun = true) + public void setUpBeforeClass() + { + locale = Locale.getDefault(); + } + + @AfterClass(alwaysRun = true) + public void tearDownAfterClass() + { + Locale.setDefault(locale); + } + + /** + * Test that saved date format does not vary with current locale + */ + @Test(groups = "Functional") + public void testSetDateProperty() + { + Date now = new Date(); + Locale.setDefault(Locale.FRENCH); + String formattedDate = Cache.setDateProperty("test", now); + Locale.setDefault(Locale.UK); + String formattedDate2 = Cache.setDateProperty("test", now); + assertEquals(formattedDate, formattedDate2); + + // currently using Locale.UK to format dates: + assertEquals( + formattedDate2, + SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.MEDIUM, + SimpleDateFormat.MEDIUM, Locale.UK).format(now)); + } +} diff --git a/test/jalview/controller/AlignViewControllerTest.java b/test/jalview/controller/AlignViewControllerTest.java new file mode 100644 index 0000000..3eefada --- /dev/null +++ b/test/jalview/controller/AlignViewControllerTest.java @@ -0,0 +1,100 @@ +package jalview.controller; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertTrue; + +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceGroup; +import jalview.datamodel.SequenceI; + +import java.util.BitSet; + +import org.testng.annotations.Test; + +public class AlignViewControllerTest +{ + @Test(groups = "Functional") + public void testFindColumnsWithFeature() + { + SequenceI seq1 = new Sequence("seq1", "aMMMaaaaaaaaaaaaaaaa"); + SequenceI seq2 = new Sequence("seq2", "aaaMMMMMMMaaaaaaaaaa"); + SequenceI seq3 = new Sequence("seq3", "aaaaaaaaaaMMMMMaaaaa"); + SequenceI seq4 = new Sequence("seq3", "aaaaaaaaaaaaaaaaaaaa"); + + /* + * features start/end are base 1 + */ + seq1.addSequenceFeature(new SequenceFeature("Metal", "desc", 2, 4, 0f, + null)); + seq1.addSequenceFeature(new SequenceFeature("Helix", "desc", 1, 15, 0f, + null)); + seq2.addSequenceFeature(new SequenceFeature("Metal", "desc", 4, 10, 0f, + null)); + seq3.addSequenceFeature(new SequenceFeature("Metal", "desc", 11, 15, + 0f, null)); + + /* + * select the first three columns --> Metal in seq1 2-3 + */ + SequenceGroup sg = new SequenceGroup(); + sg.setStartRes(0); // base 0 + sg.setEndRes(2); + sg.addSequence(seq1, false); + sg.addSequence(seq2, false); + sg.addSequence(seq3, false); + sg.addSequence(seq4, false); + + BitSet bs = new BitSet(); + int seqCount = AlignViewController.findColumnsWithFeature("Metal", sg, + bs); + assertEquals(1, seqCount); + assertEquals(2, bs.cardinality()); + assertTrue(bs.get(1)); + assertTrue(bs.get(2)); + + /* + * select the first four columns: Metal in seq1 2:4, seq2 4:4 + */ + sg.setEndRes(3); + bs.clear(); + seqCount = AlignViewController.findColumnsWithFeature("Metal", sg, + bs); + assertEquals(2, seqCount); + assertEquals(3, bs.cardinality()); + assertTrue(bs.get(1)); + assertTrue(bs.get(2)); + assertTrue(bs.get(3)); + + /* + * select column 11: Metal in seq3 only + */ + sg.setStartRes(10); + sg.setEndRes(10); + bs.clear(); + seqCount = AlignViewController.findColumnsWithFeature("Metal", sg, bs); + assertEquals(1, seqCount); + assertEquals(1, bs.cardinality()); + assertTrue(bs.get(10)); + + /* + * select columns 16-20: no Metal feature + */ + sg.setStartRes(15); + sg.setEndRes(19); + bs.clear(); + seqCount = AlignViewController.findColumnsWithFeature("Metal", sg, bs); + assertEquals(0, seqCount); + assertEquals(0, bs.cardinality()); + + /* + * look for a feature that isn't there + */ + sg.setStartRes(0); + sg.setEndRes(19); + bs.clear(); + seqCount = AlignViewController.findColumnsWithFeature("Pfam", sg, bs); + assertEquals(0, seqCount); + assertEquals(0, bs.cardinality()); + } +} diff --git a/test/jalview/datamodel/AlignedCodonFrameTest.java b/test/jalview/datamodel/AlignedCodonFrameTest.java index cd8a1e3..f2dd968 100644 --- a/test/jalview/datamodel/AlignedCodonFrameTest.java +++ b/test/jalview/datamodel/AlignedCodonFrameTest.java @@ -451,4 +451,30 @@ public class AlignedCodonFrameTest assertArrayEquals(new int[] { 2, 2 }, acf.getMappedRegion(seq2, seq1, 6)); } + + /** + * Tests for addMap. See also tests for MapList.addMapList + */ + @Test(groups = { "Functional" }) + public void testAddMap() + { + final Sequence seq1 = new Sequence("Seq1", "c-G-TA-gC-gT-T"); + seq1.createDatasetSequence(); + final Sequence aseq1 = new Sequence("Seq1", "-V-L"); + aseq1.createDatasetSequence(); + + AlignedCodonFrame acf = new AlignedCodonFrame(); + MapList map = new MapList(new int[] { 2, 4, 6, 6, 8, 9 }, new int[] { + 1, 2 }, 3, 1); + acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map); + assertEquals(1, acf.getMappingsFromSequence(seq1).size()); + Mapping before = acf.getMappingsFromSequence(seq1).get(0); + + /* + * add the same map again, verify it doesn't get duplicated + */ + acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map); + assertEquals(1, acf.getMappingsFromSequence(seq1).size()); + assertSame(before, acf.getMappingsFromSequence(seq1).get(0)); + } } diff --git a/test/jalview/datamodel/AlignmentAnnotationTests.java b/test/jalview/datamodel/AlignmentAnnotationTests.java index 271162b..1aff519 100644 --- a/test/jalview/datamodel/AlignmentAnnotationTests.java +++ b/test/jalview/datamodel/AlignmentAnnotationTests.java @@ -219,4 +219,65 @@ public class AlignmentAnnotationTests assertEquals(1, ann.annotations[1].value, 0.001); assertEquals(2, ann.annotations[2].value, 0.001); } -} + + /** + * Test the method that defaults rna symbol to the one matching the preceding + * unmatched opening bracket (if any) + */ + @Test(groups = { "Functional" }) + public void testGetDefaultRnaHelixSymbol() + { + AlignmentAnnotation ann = new AlignmentAnnotation("SS", + "secondary structure", null); + assertEquals("(", ann.getDefaultRnaHelixSymbol(4)); + + Annotation[] anns = new Annotation[20]; + ann.annotations = anns; + assertEquals("(", ann.getDefaultRnaHelixSymbol(4)); + + anns[1] = new Annotation("(", "S", '(', 0f); + assertEquals("(", ann.getDefaultRnaHelixSymbol(0)); + assertEquals("(", ann.getDefaultRnaHelixSymbol(1)); + assertEquals(")", ann.getDefaultRnaHelixSymbol(2)); + assertEquals(")", ann.getDefaultRnaHelixSymbol(3)); + + /* + * .(.[.{.<.}.>.).]. + */ + anns[1] = new Annotation("(", "S", '(', 0f); + anns[3] = new Annotation("[", "S", '[', 0f); + anns[5] = new Annotation("{", "S", '{', 0f); + anns[7] = new Annotation("<", "S", '<', 0f); + anns[9] = new Annotation("}", "S", '}', 0f); + anns[11] = new Annotation(">", "S", '>', 0f); + anns[13] = new Annotation(")", "S", ')', 0f); + anns[15] = new Annotation("]", "S", ']', 0f); + + String expected = "(())]]}}>>>>]]]]("; + for (int i = 0; i < expected.length(); i++) + { + assertEquals("column " + i, String.valueOf(expected.charAt(i)), + ann.getDefaultRnaHelixSymbol(i)); + } + + /* + * .(.[.(.).{.}.<.].D. + */ + anns[1] = new Annotation("(", "S", '(', 0f); + anns[3] = new Annotation("[", "S", '[', 0f); + anns[5] = new Annotation("(", "S", '(', 0f); + anns[7] = new Annotation(")", "S", ')', 0f); + anns[9] = new Annotation("{", "S", '{', 0f); + anns[11] = new Annotation("}", "S", '}', 0f); + anns[13] = new Annotation("<", "S", '>', 0f); + anns[15] = new Annotation("]", "S", ']', 0f); + anns[17] = new Annotation("D", "S", 'D', 0f); + + expected = "(())]]))]]}}]]>>>>dd"; + for (int i = 0; i < expected.length(); i++) + { + assertEquals("column " + i, String.valueOf(expected.charAt(i)), + ann.getDefaultRnaHelixSymbol(i)); + } + } +} \ No newline at end of file diff --git a/test/jalview/datamodel/AlignmentTest.java b/test/jalview/datamodel/AlignmentTest.java index 5a45176..7ad9436 100644 --- a/test/jalview/datamodel/AlignmentTest.java +++ b/test/jalview/datamodel/AlignmentTest.java @@ -27,6 +27,7 @@ import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertSame; import static org.testng.AssertJUnit.assertTrue; +import jalview.datamodel.AlignedCodonFrame.SequenceToSequenceMapping; import jalview.io.AppletFormatAdapter; import jalview.io.FormatAdapter; import jalview.util.MapList; @@ -37,6 +38,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -101,6 +103,460 @@ public class AlignmentTest return a; } + /** + * assert wrapper: tests all references in the given alignment are consistent + * + * @param alignment + */ + public static void assertAlignmentDatasetRefs(AlignmentI alignment) + { + verifyAlignmentDatasetRefs(alignment, true, null); + } + + /** + * assert wrapper: tests all references in the given alignment are consistent + * + * @param alignment + * @param message + * - prefixed to any assert failed messages + */ + public static void assertAlignmentDatasetRefs(AlignmentI alignment, + String message) + { + verifyAlignmentDatasetRefs(alignment, true, message); + } + + /** + * verify sequence and dataset references are properly contained within + * dataset + * + * @param alignment + * - the alignmentI object to verify (either alignment or dataset) + * @param raiseAssert + * - when set, testng assertions are raised. + * @param message + * - null or a string message to prepend to the assert failed messages. + * @return true if alignment references were in order, otherwise false. + */ + public static boolean verifyAlignmentDatasetRefs(AlignmentI alignment, + boolean raiseAssert, String message) + { + if (message==null) { message = ""; } + if (alignment == null) + { + if (raiseAssert) + { + Assert.fail(message+"Alignment for verification was null."); + } + return false; + } + if (alignment.getDataset() != null) + { + AlignmentI dataset = alignment.getDataset(); + // check all alignment sequences have their dataset within the dataset + for (SequenceI seq : alignment.getSequences()) + { + SequenceI seqds = seq.getDatasetSequence(); + if (seqds.getDatasetSequence() != null) + { + if (raiseAssert) + { + Assert.fail(message+" Alignment contained a sequence who's dataset sequence has a second dataset reference."); + } + return false; + } + if (dataset.findIndex(seqds) == -1) + { + if (raiseAssert) + { + Assert.fail(message+" Alignment contained a sequence who's dataset sequence was not in the dataset."); + } + return false; + } + } + return verifyAlignmentDatasetRefs(alignment.getDataset(), raiseAssert, message); + } + else + { + int dsp = -1; + // verify all dataset sequences + for (SequenceI seqds : alignment.getSequences()) + { + dsp++; + if (seqds.getDatasetSequence() != null) + { + if (raiseAssert) + { + Assert.fail(message+" Dataset contained a sequence with non-null dataset reference (ie not a dataset sequence!)"); + } + return false; + } + int foundp = alignment.findIndex(seqds); + if (foundp != dsp) + { + if (raiseAssert) + { + Assert.fail(message + + " Dataset sequence array contains a reference at " + + dsp + " to a sequence first seen at " + foundp + " (" + + seqds.toString() + ")"); + } + return false; + } + if (seqds.getDBRefs() != null) + { + for (DBRefEntry dbr : seqds.getDBRefs()) + { + if (dbr.getMap() != null) + { + SequenceI seqdbrmapto = dbr.getMap().getTo(); + if (seqdbrmapto != null) + { + if (seqdbrmapto.getDatasetSequence() != null) + { + if (raiseAssert) + { + Assert.fail(message+" DBRefEntry for sequence in alignment had map to sequence which was not a dataset sequence"); + } + return false; + + } + if (alignment.findIndex(dbr.getMap().getTo()) == -1) + { + if (raiseAssert) + { + Assert.fail(message+" DBRefEntry for sequence in alignment had map to sequence not in dataset"); + } + return false; + } + } + } + } + } + } + // finally, verify codonmappings involve only dataset sequences. + if (alignment.getCodonFrames() != null) + { + for (AlignedCodonFrame alc : alignment.getCodonFrames()) + { + for (SequenceToSequenceMapping ssm : alc.getMappings()) + { + if (ssm.getFromSeq().getDatasetSequence() != null) + { + if (raiseAssert) + { + Assert.fail(message+" CodonFrame-SSM-FromSeq is not a dataset sequence"); + } + return false; + } + if (alignment.findIndex(ssm.getFromSeq()) == -1) + { + + if (raiseAssert) + { + Assert.fail(message+" CodonFrame-SSM-FromSeq is not contained in dataset"); + } + return false; + } + if (ssm.getMapping().getTo().getDatasetSequence() != null) + { + if (raiseAssert) + { + Assert.fail(message+" CodonFrame-SSM-Mapping-ToSeq is not a dataset sequence"); + } + return false; + } + if (alignment.findIndex(ssm.getMapping().getTo()) == -1) + { + + if (raiseAssert) + { + Assert.fail(message+" CodonFrame-SSM-Mapping-ToSeq is not contained in dataset"); + } + return false; + } + } + } + } + } + return true; // all relationships verified! + } + + /** + * call verifyAlignmentDatasetRefs with and without assertion raising enabled, + * to check expected pass/fail actually occurs in both conditions + * + * @param al + * @param expected + * @param msg + */ + private void assertVerifyAlignment(AlignmentI al, boolean expected, + String msg) + { + if (expected) + { + try + { + + Assert.assertTrue(verifyAlignmentDatasetRefs(al, true, null), + "Valid test alignment failed when raiseAsserts enabled:" + + msg); + } catch (AssertionError ae) + { + ae.printStackTrace(); + Assert.fail( + "Valid test alignment raised assertion errors when raiseAsserts enabled: " + + msg, ae); + } + // also check validation passes with asserts disabled + Assert.assertTrue(verifyAlignmentDatasetRefs(al, false, null), + "Valid test alignment tested false when raiseAsserts disabled:" + + msg); + } + else + { + boolean assertRaised = false; + try + { + verifyAlignmentDatasetRefs(al, true, null); + } catch (AssertionError ae) + { + // expected behaviour + assertRaised = true; + } + if (!assertRaised) + { + Assert.fail("Invalid test alignment passed when raiseAsserts enabled:" + + msg); + } + // also check validation passes with asserts disabled + Assert.assertFalse(verifyAlignmentDatasetRefs(al, false, null), + "Invalid test alignment tested true when raiseAsserts disabled:" + + msg); + } + } + @Test(groups = { "Functional" }) + public void testVerifyAlignmentDatasetRefs() + { + SequenceI sq1 = new Sequence("sq1", "ASFDD"), sq2 = new Sequence("sq2", + "TTTTTT"); + + // construct simple valid alignment dataset + Alignment al = new Alignment(new SequenceI[] { + sq1, sq2 }); + // expect this to pass + assertVerifyAlignment(al, true, "Simple valid alignment didn't verify"); + + // check test for sequence->datasetSequence validity + sq1.setDatasetSequence(sq2); + assertVerifyAlignment( + al, + false, + "didn't detect dataset sequence with a dataset sequence reference."); + + sq1.setDatasetSequence(null); + assertVerifyAlignment( + al, + true, + "didn't reinstate validity after nulling dataset sequence dataset reference"); + + // now create dataset and check again + al.createDatasetAlignment(); + assertNotNull(al.getDataset()); + + assertVerifyAlignment(al, true, + "verify failed after createDatasetAlignment"); + + // create a dbref on sq1 with a sequence ref to sq2 + DBRefEntry dbrs1tos2 = new DBRefEntry("UNIPROT", "1", "Q111111"); + dbrs1tos2.setMap(new Mapping(sq2.getDatasetSequence(), + new int[] { 1, 5 }, new int[] { 2, 6 }, 1, 1)); + sq1.getDatasetSequence().addDBRef(dbrs1tos2); + assertVerifyAlignment(al, true, + "verify failed after addition of valid DBRefEntry/map"); + // now create a dbref on a new sequence which maps to another sequence + // outside of the dataset + SequenceI sqout = new Sequence("sqout", "ututututucagcagcag"), sqnew = new Sequence( + "sqnew", "EEERRR"); + DBRefEntry sqnewsqout = new DBRefEntry("ENAFOO", "1", "R000001"); + sqnewsqout.setMap(new Mapping(sqout, new int[] { 1, 6 }, new int[] { 1, + 18 }, 1, 3)); + al.getDataset().addSequence(sqnew); + + assertVerifyAlignment(al, true, + "verify failed after addition of new sequence to dataset"); + // now start checking exception conditions + sqnew.addDBRef(sqnewsqout); + assertVerifyAlignment( + al, + false, + "verify passed when a dbref with map to sequence outside of dataset was added"); + // make the verify pass by adding the outsider back in + al.getDataset().addSequence(sqout); + assertVerifyAlignment(al, true, + "verify should have passed after adding dbref->to sequence in to dataset"); + // and now the same for a codon mapping... + SequenceI sqanotherout = new Sequence("sqanotherout", + "aggtutaggcagcagcag"); + + AlignedCodonFrame alc = new AlignedCodonFrame(); + alc.addMap(sqanotherout, sqnew, new MapList(new int[] { 1, 6 }, + new int[] { 1, 18 }, 3, 1)); + + al.addCodonFrame(alc); + Assert.assertEquals(al.getDataset().getCodonFrames().size(), 1); + + assertVerifyAlignment( + al, + false, + "verify passed when alCodonFrame mapping to sequence outside of dataset was added"); + // make the verify pass by adding the outsider back in + al.getDataset().addSequence(sqanotherout); + assertVerifyAlignment( + al, + true, + "verify should have passed once all sequences involved in alCodonFrame were added to dataset"); + al.getDataset().addSequence(sqanotherout); + assertVerifyAlignment(al, false, + "verify should have failed when a sequence was added twice to the dataset"); + al.getDataset().deleteSequence(sqanotherout); + assertVerifyAlignment(al, true, + "verify should have passed after duplicate entry for sequence was removed"); + } + + /** + * checks that the sequence data for an alignment's dataset is non-redundant. + * Fails if there are sequences with same id, sequence, start, and. + */ + + public static void assertDatasetIsNormalised(AlignmentI al) + { + assertDatasetIsNormalised(al, null); + } + + /** + * checks that the sequence data for an alignment's dataset is non-redundant. + * Fails if there are sequences with same id, sequence, start, and. + * + * @param al + * - alignment to verify + * @param message + * - null or message prepended to exception message. + */ + public static void assertDatasetIsNormalised(AlignmentI al, String message) + { + if (al.getDataset()!=null) + { + assertDatasetIsNormalised(al.getDataset(), message); + return; + } + /* + * look for pairs of sequences with same ID, start, end, and sequence + */ + List seqSet = al.getSequences(); + for (int p=0;p
        + + + + false + + + true + + + true + + + + + + + + + + + + false + + + + + + true + + + true + + + true + + + + + + 16046 + + + false + + + 0 + + + + + +
        @@ -7360,6 +7412,7 @@ and any path to a file to read from that file]]>
        + @@ -7948,6 +8001,7 @@ and any path to a file to read from that file]]> + diff --git a/utils/MessageBundleChecker.java b/utils/MessageBundleChecker.java new file mode 100644 index 0000000..9d322df --- /dev/null +++ b/utils/MessageBundleChecker.java @@ -0,0 +1,404 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashSet; +import java.util.Properties; +import java.util.TreeSet; +import java.util.regex.Pattern; + +/** + * This class scans Java source files for calls to MessageManager and reports + *
          + *
        • calls using keys not found in Messages.properties
        • + *
        • any unused keys in Messages.properties
        • + *
        + * It does not handle dynamically constructed keys, these are reported as + * possible errors for manual inspection.
        + * For comparing translated bundles with Messages.properties, see i18nAnt.xml + * + * @author gmcarstairs + * + */ +public class MessageBundleChecker +{ + /* + * regex ^"[^"]*"$ + * opening quote, closing quote, no quotes in between + */ + static Pattern STRING_PATTERN = Pattern.compile("^\"[^\"]*\"$"); + + /* + * number of text lines to read at a time in order to parse + * code that is split over several lines + */ + static int bufferSize = 3; + + /* + * resource bundle key is arg0 for these methods + */ + static final String METHOD1 = "MessageManager.getString("; + + static final String METHOD2 = "MessageManager.formatMessage("; + + static final String METHOD3 = "MessageManager.getStringOrReturn("; + + /* + * resource bundle key is arg1 for this method + */ + static final String JVINIT = "JvSwingUtils.jvInitComponent("; + + static final String[] METHODS = { METHOD1, METHOD2, METHOD3, JVINIT }; + + /* + * root of the Java source folders we want to scan + */ + String sourcePath; + + /* + * contents of Messages.properties + */ + private Properties messages; + + /* + * keys from Messages.properties + * we remove entries from here as they are found to be used + * any left over are unused entries + */ + private TreeSet messageKeys; + + private int javaCount; + + private HashSet invalidKeys; + + /** + * Runs the scan given the path to the root of Java source directories + * + * @param args + * [0] path to the source folder to scan + * @param args + * [1] (optional) read buffer size (default is 3); increasing this + * may detect more results but will give higher error counts due to + * double counting of the same code + * @throws IOException + */ + public static void main(String[] args) throws IOException + { + if (args.length != 1 && args.length != 2) + { + System.out.println("Usage: [readBufferSize]"); + return; + } + if (args.length == 2) + { + bufferSize = Integer.valueOf(args[1]); + } + new MessageBundleChecker().doMain(args[0]); + } + + /** + * Main method to perform the work + * + * @param srcPath + * @throws IOException + */ + private void doMain(String srcPath) throws IOException + { + System.out.println("Scanning " + srcPath + + " for calls to MessageManager"); + sourcePath = srcPath; + loadMessages(); + File dir = new File(srcPath); + if (!dir.exists()) + { + System.out.println(srcPath + " not found"); + return; + } + invalidKeys = new HashSet(); + if (dir.isDirectory()) + { + scanDirectory(dir); + } + else + { + scanFile(dir); + } + reportResults(); + } + + /** + * Prints out counts to sysout + */ + private void reportResults() + { + System.out.println("\nScanned " + javaCount + " source files"); + System.out.println("Message.properties has " + messages.size() + + " keys"); + System.out.println("Found " + invalidKeys.size() + + " possibly invalid parameter calls"); + + System.out.println(messageKeys.size() + + " keys not found, either unused or constructed dynamically"); + for (String key : messageKeys) + { + System.out.println(" " + key); + } + } + + /** + * Scan all files within a directory + * + * @param dir + * @throws IOException + */ + private void scanDirectory(File dir) throws IOException + { + File[] files = dir.listFiles(); + if (files != null) + { + for (File f : files) + { + if (f.isDirectory()) + { + scanDirectory(f); + } + else + { + scanFile(f); + } + } + } + } + + /** + * Scan a Java file + * + * @param f + */ + private void scanFile(File f) throws IOException + { + String path = f.getPath(); + if (!path.endsWith(".java")) + { + return; + } + javaCount++; + + /* + * skip class with designed dynamic lookup call + */ + if (path.endsWith("gui/JvSwingUtils.java")) + { + return; + } + + String[] lines = new String[bufferSize]; + BufferedReader br = new BufferedReader(new FileReader(f)); + for (int i = 0; i < bufferSize; i++) + { + String readLine = br.readLine(); + lines[i] = stripCommentsAndTrim(readLine); + } + + int lineNo = 0; + + while (lines[bufferSize - 1] != null) + { + lineNo++; + inspectSourceLines(path, lineNo, lines); + + for (int i = 0; i < bufferSize - 1; i++) + { + lines[i] = lines[i + 1]; + } + lines[bufferSize - 1] = stripCommentsAndTrim(br.readLine()); + } + br.close(); + + } + + /* + * removes anything after (and including) '//' + */ + private String stripCommentsAndTrim(String line) + { + if (line != null) + { + int pos = line.indexOf("//"); + if (pos != -1) + { + line = line.substring(0, pos); + } + line = line.replace("\t", " ").trim(); + } + return line; + } + + /** + * Look for calls to MessageManager methods, possibly split over two or more + * lines + * + * @param path + * @param lineNo + * @param lines + */ + private void inspectSourceLines(String path, int lineNo, String[] lines) + { + String lineNos = String.format("%d-%d", lineNo, lineNo + lines.length + - 1); + String combined = combineLines(lines); + for (String method : METHODS) + { + int pos = combined.indexOf(method); + if (pos == -1) + { + continue; + } + + /* + * extract what follows the opening bracket of the method call + */ + String methodArgs = combined.substring(pos + method.length()).trim(); + if ("".equals(methodArgs)) + { + /* + * arguments are on next line - catch in the next read loop iteration + */ + continue; + } + if (methodArgs.indexOf(",") == -1 && methodArgs.indexOf(")") == -1) + { + /* + * arguments continue on next line - catch in the next read loop iteration + */ + continue; + } + + if (JVINIT == method && methodArgs.indexOf(",") == -1) + { + /* + * not interested in 1-arg calls to jvInitComponent + */ + continue; + } + + if (METHOD3 == method) + { + System.out.println(String.format("Dynamic key at %s line %s %s", + path.substring(sourcePath.length()), lineNos, combined)); + continue; + } + + String messageKey = getMessageKey(method, methodArgs); + if (messageKey == null) + { + System.out.println(String.format("Trouble parsing %s line %s %s", + path.substring(sourcePath.length()), lineNos, combined)); + continue; + } + + if (!(STRING_PATTERN.matcher(messageKey).matches())) + { + System.out.println(String.format("Dynamic key at %s line %s %s", + path.substring(sourcePath.length()), lineNos, combined)); + continue; + } + + /* + * strip leading and trailing quote + */ + messageKey = messageKey.substring(1, messageKey.length() - 1); + + if (!this.messages.containsKey(messageKey)) + { + System.out.println(String.format( + "Unmatched key '%s' at line %s of %s", messageKey, lineNos, + path.substring(sourcePath.length()))); + if (!invalidKeys.contains(messageKey)) + { + invalidKeys.add(messageKey); + } + } + messageKeys.remove(messageKey); + } + } + + /** + * Helper method to parse out the resource bundle key parameter of a method + * call + * + * @param method + * @param methodArgs + * the rest of the source line starting with arguments to method + * @return + */ + private String getMessageKey(String method, String methodArgs) + { + String key = methodArgs; + + /* + * locate second argument if calling jvInitComponent() + */ + if (method == JVINIT) + { + int commaLoc = methodArgs.indexOf(","); + if (commaLoc == -1) + { + return null; + } + key = key.substring(commaLoc + 1).trim(); + } + + /* + * take up to next comma or ) or end of line + */ + int commaPos = key.indexOf(","); + int bracePos = key.indexOf(")"); + int endPos = commaPos == -1 ? bracePos : (bracePos == -1 ? commaPos + : Math.min(commaPos, bracePos)); + if (endPos == -1 && key.length() > 1 && key.endsWith("\"")) + { + endPos = key.length(); + } + + return endPos == -1 ? null : key.substring(0, endPos); + } + + private String combineLines(String[] lines) + { + String combined = ""; + if (lines != null) + { + for (String line : lines) + { + if (line != null) + { + combined += line; + } + } + } + return combined; + } + + /** + * Loads properties from Message.properties + * + * @throws IOException + */ + void loadMessages() throws IOException + { + messages = new Properties(); + FileReader reader = new FileReader(new File(sourcePath, + "../resources/lang/Messages.properties")); + messages.load(reader); + reader.close(); + + messageKeys = new TreeSet(); + for (Object key : messages.keySet()) + { + messageKeys.add((String) key); + } + + } + +} diff --git a/utils/checkstyle/README.txt b/utils/checkstyle/README.txt new file mode 100644 index 0000000..e38064e --- /dev/null +++ b/utils/checkstyle/README.txt @@ -0,0 +1,85 @@ +Checkstyle for Jalview +---------------------- + +http://checkstyle.sourceforge.net/ +GNU LGPL + +To get the Eclipse Checkstyle plugin +------------------------------------ + - Help | Eclipse Marketplace + - search for checkstyle + - install eclipse-cs checkstyle plugin +The current version is 6.19.1 (August 2016). + +Config +------ + + File Jalview/.checkstyle holds configuration for the "JalviewCheckstyle" ruleset. + This includes confining its scope to src/*.java and resources/*.properties. + This can be modified interactively through the checkstyle properties editor. + + Checkstyle config files in utils/checkstyle: + checkstyle.xml : main configuration file with selected checkstyle modules + checkstyle-suppress.xml : rules to exclude certain checks / files + import-control.xml : package import rules + + Checkstyle error messages can be customised. See TypeName for an example. + +How to use checkstyle +--------------------- + + Option 1: enable it for the Jalview project + - right-click on project | Checkstyle | Activate Checkstyle + - notice CheckstyleNature gets added to the .project file + - don't commit this file unless we all agree to! + - Checkstyle will run as you recompile changed code + + Option 2: on demand on selected code + - right-click on a class or package and Checkstyle | Check code with checkstyle + - (or Clear Checkstyle violations to remove checkstyle warnings) + +Checkstyle rules +---------------- + Documented at http://checkstyle.sourceforge.net/checks.html + Should be self-documenting in checkstyle.xml + Open for discussion: + - which rules to use + - what naming and layout standards to apply + - settings for complexity metrics + - whether any rules should report an error instead of a warning + +Suppressing findings +-------------------- + If there are warnings you judge it ok to suppress (false positives), + your options are (from most global to most local impact): + - remove the rule entirely + - adjust its properties + - add an entry in checkstyle-suppress.xml to skip the file for the rule + - add comments around the reported source lines + // CHECKSTYLE.OFF: RuleName 'a comment to justify suppression' + source code here + // CHECKSTYLE.ON: RuleName + The suppression should be as localised as possible, to avoid false negatives. + +Tips +---- + Sometimes checkstyle needs a kick before it will refresh its findings. + A whitespace edit in checkstyle.xml usually does this. There may be better ways. + + Invalid configuration files may result in checkstyle failing with an error reported + in the Eclipse log file. + Help | Installation Details | Configuration takes you to a screen with a + 'View Error Log' button. + + Sometimes checkstyle can fail silently. Try 'touching' (editing) config files, failing + that, carefully check / back out / redo any recent changes to its config. + + Putting inside a checkstyle causes it to be ignored! + + If a rule doesn't behave as you expected, read its documentation carefully, including + the use and default value of any properties. + + To highlight a single rule's findings, you can 'Configure Contents' of the Problems view + and filter on Text Contains (case-sensitive). + Here you should select 'Use item limits' with a value of, say, 500, or Eclipse may + labour to display all warnings. diff --git a/utils/checkstyle/checkstyle-suppress.xml b/utils/checkstyle/checkstyle-suppress.xml new file mode 100644 index 0000000..ac9e260 --- /dev/null +++ b/utils/checkstyle/checkstyle-suppress.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utils/checkstyle/checkstyle.xml b/utils/checkstyle/checkstyle.xml new file mode 100644 index 0000000..85ac8e6 --- /dev/null +++ b/utils/checkstyle/checkstyle.xml @@ -0,0 +1,607 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utils/checkstyle/import-control.xml b/utils/checkstyle/import-control.xml new file mode 100644 index 0000000..b41aab3 --- /dev/null +++ b/utils/checkstyle/import-control.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utils/i18nAnt.xml b/utils/i18nAnt.xml index 47c404e..01973d2 100755 --- a/utils/i18nAnt.xml +++ b/utils/i18nAnt.xml @@ -19,8 +19,7 @@ var logger = project.getBuildListeners( ).firstElement( ); logger.setMessageOutputLevel( 1 ); - - + @@ -29,15 +28,28 @@ + + + + + + + + + + + + + + + - + - -
  • +
    -nonews
    +
    +
    Disable check for Jalview news on startup (not recommended other than for classroom / demo usage)
    +
    -nousagestats
    Turn off google analytics usage tracking
    diff --git a/help/html/features/dasfeatures.html b/help/html/features/dasfeatures.html index c0c888a..32408ee 100644 --- a/help/html/features/dasfeatures.html +++ b/help/html/features/dasfeatures.html @@ -29,8 +29,7 @@

    Jalview includes a client for retrieving sequences and their - features via the Distributed - Annotation System. + features via the Distributed Annotation System.

    1. Open the Feature Settings panel by selecting "View @@ -51,11 +50,11 @@

    - If your DAS source selection contains sources which use Uniprot - accession ids, you will be asked whether Jalview should find Uniprot + If your DAS source selection contains sources which use UniProt + accession ids, you will be asked whether Jalview should find UniProt Accession ids for the given sequence names. It is important to - realise that many DAS sources only use Uniprot accession ids, rather - than Swissprot/Uniprot sequence names.
    The The database reference fetcher documentation describes how Jalview discovers what database references are appropriate for the sequences @@ -69,6 +68,10 @@

    DAS support was introduced in Jalview Version 2.1.

    +
    +

    + The DAS registry at http://www.dasregistry.org was decommissioned early in 2015. An unmaintained mirror is currently hosted at http://www.ebi.ac.uk/das-srv/registry/. +

      diff --git a/help/html/features/ensemblsequencefetcher.html b/help/html/features/ensemblsequencefetcher.html new file mode 100644 index 0000000..2a62caa --- /dev/null +++ b/help/html/features/ensemblsequencefetcher.html @@ -0,0 +1,56 @@ + + + +Fetching ENSEMBL Data in Jalview + + + + Fetching ENSEMBL Data in Jalview +
    Jalview Version 2.10 (September 2016) introduced support to + retrieve annotated transcripts, peptides and genomic contigs from + ENSEMBL. +
    + Database selection dialog for fetching sequences (introduced in Jalview 2.8) + +

    Two types of ENSEMBL source are provided. ENSEMBL queries the + main ENSEMBL site, which only serves data for higher eukaryotes, and + EnsemblGenomes, which provides access to Ensembl Pathogens, and + other warehouses.

    +

    General Use
    If you have a set of Ensembl + peptide or transcript IDs, then you can retrieve them + via the sequence fetcher dialog opened after selecting the + most appropriate source (either 'ENSEMBL', or Ensembl Genomes). + However, Jalview's Ensembl client has a couple of additional + capabilities:

    Retrieving aligned transcripts for a genomic ID +

    +

    If a single genomic identifier is entered in the + Ensembl fetcher, Jalview will return all transcripts and products + for the locus, and display them in a split view - complete with + sequence variant annotation.

    +

    Retrieving orthologs for a gene ID

    +

    If a gene ID is entered (e.g. fox1), Jalview will resolve + Ensembl genomic identifiers for a predefined set of taxa (Mouse, + Rat, Human, Yeast in Jalview 2.10). +

    + + \ No newline at end of file diff --git a/help/html/features/featuresettings.html b/help/html/features/featuresettings.html index 9164afd..200fc8f 100755 --- a/help/html/features/featuresettings.html +++ b/help/html/features/featuresettings.html @@ -104,7 +104,7 @@ the bottom of the list is rendered below a feature higher up in the list.
    You can change the order of a feature by dragging it up and down the list with - the mouse. + the mouse (not applet).

    The Optimise order button (currently only diff --git a/help/html/features/groovy.html b/help/html/features/groovy.html index 9aa341b..a2bc627 100644 --- a/help/html/features/groovy.html +++ b/help/html/features/groovy.html @@ -56,6 +56,9 @@

    + Executing a groovy script on a particular alignment
    + +

    Access to Jalview's functions from Groovy Scripts
    There is as yet no properly defined scripting interface to Jalview, but all the public methods of the jalview class hierarchy can be diff --git a/help/html/features/jmol.html b/help/html/features/jmol.html index 3fa1563..3141aae 100644 --- a/help/html/features/jmol.html +++ b/help/html/features/jmol.html @@ -195,7 +195,7 @@ Colours each residue in the structure with the colour of its corresponding residue in the associated sequence as rendered in the associated alignment views, including any - Uniprot sequence features or region colourings.
    Pick + UniProt sequence features or region colourings.
    Pick which of the associated alignment views are used to colour the structures using the View→Colour by .. sub menu. diff --git a/help/html/features/mmcif.html b/help/html/features/mmcif.html new file mode 100644 index 0000000..e67f5cd --- /dev/null +++ b/help/html/features/mmcif.html @@ -0,0 +1,34 @@ + + + + +mmCIF File Format + + + What is mmCIF ? +
    mmCIF stands for 'macro-molecular Crystallographic Information + File'. This format was developed by the PDB consortium and the + International Union of Crystallography (IUCr), based on + Crystallographic Information File (CIF), a format used for describing + the structures of small molecules.
    mmCIF became the recommended format + for the exchange of biomacromolecular structures in 2014. +

    + mmCIF and Jalview
    Since Jalview 2.10, mmCIF + is used for structures downloaded from the PDB. This means: +

    +
      +
    1. Jalview can use the mmCIF to read structures that are too + large to be represented by a single PDB file. (ie, with more than + >62 chains and/or 99999 ATOM records).
    2. +
    3. Jalview users will have access to the richer annotation + provided by PDBx/mmCIF files.
    4. +
    5. There may be slight differences between sequences + containing modified residues for structures downloaded in mmCIF + format. This is because mmCIF differs from the PDB format in the + way it describes non-standard sequence data.
    6. +
    + + Support for importing 3D structure data from flat file and + EMBL-PDBe as mmCIF was added in Jalview 2.10 + + \ No newline at end of file diff --git a/help/html/features/pdbviewer.html b/help/html/features/pdbviewer.html index fd13f57..eca218a 100755 --- a/help/html/features/pdbviewer.html +++ b/help/html/features/pdbviewer.html @@ -123,7 +123,7 @@ Colours each residue in the structure with the colour of its corresponding residue in the associated sequence as rendered in the associated alignment view, including any - Uniprot sequence features or region colourings.
    + UniProt sequence features or region colourings.
    Residues which only exist in the PDB structure are coloured white if they are insertions (relative to the associated sequence in the alignment) and grey if they are N or C diff --git a/help/html/features/selectfetchdb.gif b/help/html/features/selectfetchdb.gif index 6e1ce10..de0a7e6 100644 Binary files a/help/html/features/selectfetchdb.gif and b/help/html/features/selectfetchdb.gif differ diff --git a/help/html/features/seqfeatures.html b/help/html/features/seqfeatures.html index 0ec5f3b..cf79858 100755 --- a/help/html/features/seqfeatures.html +++ b/help/html/features/seqfeatures.html @@ -29,7 +29,7 @@

    Jalview can colour parts of a sequence based on the presence of sequence features - which may be retrieved from database records - (such as Uniprot), the result of sequence + (such as UniProt), the result of sequence motif searches or simply read from a sequence features file. You can also create features from the results of searches or the current selection, @@ -54,7 +54,7 @@

    Since Jalview 2.08, sequence features assigned to a sequence can be organised into groups, which may indicate that the features were all - retrieved from the same database (such as Uniprot features), or + retrieved from the same database (such as UniProt features), or generated by the same analysis process (as might be specified in a sequence features file). diff --git a/help/html/features/seqfetch.html b/help/html/features/seqfetch.html index 4aa7234..04d3c1d 100755 --- a/help/html/features/seqfetch.html +++ b/help/html/features/seqfetch.html @@ -32,10 +32,7 @@ Institute, or, since Jalview 2.4, DAS servers capable of the sequence command (configured in DAS settings).

    - The Jalview Sequence Fetcher Dialog Box -

    The Sequence Fetcher dialog box can be opened via the +

    The Sequence Fetcher can be opened via the "File" menu on the main desktop in order to retrieve sequences as a new alignment, or opened via the "File" menu of an existing alignment to import additional sequences. There @@ -43,51 +40,60 @@ whilst Jalview compiles the list of available sequence datasources from the currently defined DAS server registry.

    - First, select the database you want to retrieve - sequences from by clicking the button labeled 'Select database - retrieval source'. If a database source is already selected, then - the button's label will change to show the currently selected - database. + Every time a new fetcher is opened, you will need to select the database you want to retrieve + sequences from the database chooser.

    - Database selection dialog for fetching sequences (introduced in Jalview 2.8) -

    Since Jalview 2.8, the available databases are shown as a tree - in a popup dialog box. The databases are ordered alphabetically, and - if there are many sources for the same type of sequence identifier, - they will be grouped together in a sub-branch branch labeled with - the identifier.

    -

    - Once you have selected the sequence database using the popup dialog - box, enter one or more accession ids (as a - semi-colon separated list), or press the "Example" button - to paste the example accession for the currently selected database - into the retrieval box. Finally, press "OK" to initiate - the retrieval. -

    -

    - Fetching from The PDB with the EMBL-EBI PDBe Search - Interface -

    -

    - Since Jalview 2.9, selecting PDB as the sequence database will open - the PDB Sequence Fetcher for - discovering and retrieving structures. -

    +

    The databases are shown as a tree, and ordered alphabetically; + tooltips are shown if you mouse over some sources, explaining what + the database will retrieve. You can select one by using the up/down + arrow keys and hitting return, or by double clicking with the mouse. +
    If you have DAS sources enabled, then you may have several sources + for the same type of sequence identifier, and these will be grouped + together in a sub-branch branch labeled with the identifier.

    +

    Once you have selected a sequence database, its fetcher dialog + will open. Jalview provides two types of dialog:

    +
    1. The Free-text Search Interface + +
      Free-text search clients are provided for PDB (Since 2.9), and + UniProt (Since 2.10). They provide access to each database's own + query system, enabling you to retrieve data by accession, free text + description, or any other type of supported field. For full details, + see each client's help page: + +
    2. +
    3. Accession based sequence retrieval +
      + + The Jalview Sequence Fetcher Dialog Box
      + To retrieve sequences, simply enter one or more accession ids (as a semi-colon + separated list), or press the "Example" button to paste the + example accession for the currently selected database into the + retrieval box. Finally, press "OK" to initiate the + retrieval. +
    4. +

    Only retrieving part of a sequence

    - DAS sources (indicated by a "(DAS)") allow a - range to be specified in addition to a sequence ID. To retrieve 50 - residues starting at position 35 in UNIPROT sequence P73137 using - the UNIPROT DAS server, you would enter "'P73137:35,84'.
    - Full support for DAS range queries was introduced in - Jalview 2.8 + When using DAS sources (indicated by a "(DAS)"), + you can append a range in addition to a sequence ID. For example, to + retrieve 50 residues starting at position 35 in UNIPROT sequence + P73137 using the UNIPROT DAS server, you would enter + "'P73137:35,84'.
    Full support for DAS range + queries was introduced in Jalview 2.8

    If you use the WSDBFetch sequence fetcher services (EMBL, - Uniprot, PFAM, and RFAM) in work for publication, please cite:

    + UniProt, PFAM, and RFAM) in work for publication, please cite:

    Pillai S., Silventoinen V., Kallio K., Senger M., Sobhany S., Tate J., Velankar S., Golovin A., Henrick K., Rice P., Stoehr P., Lopez diff --git a/help/html/features/seqmappings.html b/help/html/features/seqmappings.html index ac63c9e..b88aefc 100644 --- a/help/html/features/seqmappings.html +++ b/help/html/features/seqmappings.html @@ -25,7 +25,7 @@

    Mapping Between Different Sequences -

    +

    A new feature in Jalview 2.3 is the ability to map between sequences in different domains, based on alignment, or by the use of explicit mappings provided by databases.

    @@ -40,7 +40,10 @@ correspondence between DNA and protein sequences. This mapping can be imported directly from EMBL and EMBLCDS database records retrieved by the Sequence Fetcher, and - allows sequence features to be mapped directly from Uniprot das + allows sequence features to be mapped directly from UniProt das sources to their coding region on EMBL sequence records. +

    + SIFTS Mapping between PDB and + UniProt data was introduced in Jalview 2.10 diff --git a/help/html/features/sifts_mapping_output.png b/help/html/features/sifts_mapping_output.png new file mode 100644 index 0000000..ddc8a1f 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 index 0000000..80c0294 --- /dev/null +++ b/help/html/features/siftsmapping.html @@ -0,0 +1,70 @@ + + + + +SIFTS Mapping from UniProt for PDB Structures + + + +

    + SIFTS Mapping for UniProt sequences and PDB + Structures
    SIFTS (Structure Integration with Function, + Taxonomy and Sequences) is a database of residue-level mappings + between UniProt protein sequences, and protein structures found in + the PDB. The database is updated for each PDB release, and is + provided by the PDBe + at EMBL-EBI. +

    +

    When Jalview imports PDB data for a protein sequence found in + UniProt, either via the 'View 3D Structure...' option, or the 'Fetch + DB Refs' web services menu, Jalview will also download its SIFTS + record and use that information to construct a mapping between the + sequence and downloaded structure.

    +

    If, for some reason, no SIFTS mapping data exists, then Jalview + will generate a mapping using its built-in Needleman and Wunsch + global alignment algorithm. This method of mapping was used for all + structures prior to version 2.10. +

    + Controlling and troubleshooting SIFTS mappings
    + Configuration options controlling whether SIFTS mappings are used + can be found in the Tools → Preferences → + Structure tab, under 'Sequence ↔ Structure method'.
    Note: + Changing the configuration will only affect how new mappings are + created. In order to recompute mappings for structures already + loaded, please reload the sequence & structural data. +

    + +

    + Multi-Chain Mappings
    SIFTS gives Jalview the + ability to display multi-chain mappings between UniProt sequences + and PDB structure data. This is important when working with + multimeric proteins, since the biological unit will contain several + structures for the same protein sequence. Multi-chain mapping allows + all residues in a structure to be located in the alignment, and + also, when shading the structure by sequence colours, enables + conservation patterns between oligomer interfaces to be explored. +

    +

    To see this in action, load uniprot sequence for FER1_MAIZE + then veiw PDB structure for 3B2F, you will notice that mousing over + the sequence results to two positions being highlighted in the + structure, also colouring the sequence transfers the color to all + the mapped chains in the structure.

    + +

    + Viewing Mapping Output
    The mapping provided + by the SIFTS record is accessible via File → + View mapping menu of the structure viewers. The screenshot below + is the mapping output for the {FER1_MAIZE ↔ + 3B2F} example described above, and confirms that all two chains + were mapped. The mapping method used can be seen within the area + highlighted with red boarder. +

    + +  SIFTS mapping output +

    + SIFTS Mapping integration was added in Jalview 2.10 +

    + + + \ No newline at end of file diff --git a/help/html/features/splitView.html b/help/html/features/splitView.html index ed5bef3..1c36abd 100644 --- a/help/html/features/splitView.html +++ b/help/html/features/splitView.html @@ -53,7 +53,7 @@
  • On selecting rows, columns or regions in one alignment, the corresponding selection is made in the other
  • Sequence ordering in one alignment (using the cursor, or "Calculate→Sort") is also applied to the other
  • Editing (gap insertion / deletion) in the protein alignment @@ -75,7 +75,7 @@ panels.
  • Panel heights are adjusted dragging the divider between them using the mouse
  • -
  • "View→New +
  • "View→New View / Expand Views / Gather Views" behave as for a normal alignment window, but always create new views as Split Frames
  • diff --git a/help/html/features/structurechooser.html b/help/html/features/structurechooser.html index 296a751..f5f5916 100644 --- a/help/html/features/structurechooser.html +++ b/help/html/features/structurechooser.html @@ -75,7 +75,7 @@ structures using various metric categories avaialble from the meta-data of the structures. To perform this simply select any of the following options from the drop-down menu in the Structure Chooser - interface: Best Uniprot coverage, Higest Resolution, Best Quality, + interface: Best UniProt coverage, Higest Resolution, Best Quality, Highest Protein Chain etc. When the 'Invert' option is selected, Jalview returns an inverse result for the current selected option in the drop-down menu. diff --git a/help/html/features/uniprotqueryfields.html b/help/html/features/uniprotqueryfields.html new file mode 100644 index 0000000..eaee308 --- /dev/null +++ b/help/html/features/uniprotqueryfields.html @@ -0,0 +1,603 @@ + + + +UniProtKB query fields + + + +

    + UniProtKB query fields +

    +

    + Supported query fields for searching specific data in UniProtKB (see + also query + syntax). +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldExampleDescription
    accession + accession:P62988 + + Lists all entries with the primary or secondary + accession number P62988. +
    active + active:no + + Lists all obsolete entries. +
    annotation + + annotation:(type:non-positional) +
    + annotation:(type:positional) +
    + annotation:(type:mod_res "Pyrrolidone carboxylic acid" evidence:experimental) +
    +
    + Lists all entries with: +
      +
    • any general annotation (comments [CC])
    • +
    • any sequence annotation (features [FT])
    • +
    • at least one amino acid modified with a Pyrrolidone carboxylic acid group
    • +
    +
    author + + author:ashburner + + + Lists all entries with at least one reference co-authored by Michael Ashburner. +
    cdantigen + + cdantigen:CD233 + + + Lists all entries whose cluster of differentiation number is CD233. +
    citation + + citation:("intracellular structural proteins") +
    + citation:(author:ashburner journal:nature) + citation:9169874 +
    +
    + Lists all entries with a literature citation: +
      +
    • containing the phrase "intracellular structural proteins" in either title or abstract
    • +
    • co-authored by Michael Ashburner and published in Nature
    • +
    • with the PubMed identifier 9169874
    • +
    +
    cluster + + cluster:UniRef90_A5YMT3 + + + Lists all entries in the UniRef 90% identity cluster whose + representative sequence is UniProtKB entry A5YMT3. +
    count + + annotation:(type:transmem count:5)
    + annotation:(type:transmem count:[5 TO *])
    + annotation:(type:cofactor count:[3 TO *]) +
    +
    Lists all entries with: +
      +
    • exactly 5 transmembrane regions
    • +
    • 5 or more transmembrane regions
    • +
    • 3 or more Cofactor comments
    • +
    +
    created + + created:[20121001 TO *]
    + reviewed:yes AND created:[current TO *] +
    +
    + Lists all entries created since October 1st 2012.
    + Lists all new UniProtKB/Swiss-Prot entries in the last release. +
    database + + database:(type:pfam) +
    + database:(type:pdb 1aut) +
    +
    + Lists all entries with: +
      +
    • a cross-reference to the Pfam database
    • +
    • a cross-reference to the PDB database entry 1aut
    • +
    + +
    domain + + domain:VWFA + + + Lists all entries with a Von Willebrand factor type A domain described + in the 'Family and Domains' section. +
    ec + + ec:3.2.1.23 + + + Lists all beta-galactosidases. +
    evidence + + annotation:(type:signal evidence:ECO_0000269)
    + (type:mod_res phosphoserine evidence:ECO_0000269)
    + annotation:(type:function AND evidence:ECO_0000255) +
    +
    Lists all entries with: +
      +
    • a signal sequence whose positions have been experimentally proven
    • +
    • experimentally proven phosphoserine sites
    • +
    • a function manually asserted according to rules
    • +
    +
    family + + family:serpin + + + Lists all entries belonging to the Serpin family of proteins. +
    fragment + + fragment:yes + + + Lists all entries with an incomplete sequence. +
    gene + + gene:HSPC233 + + + Lists all entries for proteins encoded by gene HSPC233. +
    go + + go:cytoskeleton +
    + go:0015629 +
    +
    + Lists all entries associated with: +
      +
    • a GO term containing the word "cytoskeleton"
    • +
    • the GO term Actin cytoskeleton and any subclasses
    • +
    +
    host + + host:mouse +
    + host:10090 +
    + host:40674 +
    +
    + Lists all entries for viruses infecting: +
      +
    • organisms with a name containing the word "mouse"
    • +
    • Mus musculus (Mouse)
    • +
    • all mammals (all taxa classified under the taxonomy node for Mammalia)
    • +
    +
    id + id:P00750 + + Returns the entry with the primary + accession number P00750. +
    inn + + inn:Anakinra + + + Lists all entries whose "International Nonproprietary Name" is Anakinra. +
    interactor + + interactor:P00520 + + + Lists all entries describing interactions with the protein described by + entry P00520. +
    keyword + + keyword:toxin + + + Lists all entries associated with the keyword Toxin. +
    length + + length:[500 TO 700] + + + Lists all entries describing sequences of length between 500 and 700 residues. +
    lineage + + This field is a synonym for the field taxonomy. +
    mass + + mass:[500000 TO *] + + + Lists all entries describing sequences with a mass of at least 500,000 Da. +
    method + + method:maldi +
    + method:xray +
    +
    + Lists all entries for proteins identified by: matrix-assisted laser + desorption/ionization (MALDI), crystallography (X-Ray). The + method field searches names of physico-chemical + identification methods in the 'Biophysicochemical properties' subsection of the 'Function' section, the 'Publications' and + 'Cross-references' sections. +
    mnemonic + + mnemonic:ATP6_HUMAN + + + Lists all entries with entry name (ID) ATP6_HUMAN. Searches also + obsolete entry names. +
    modified + + modified:[20120101 TO 20120301]
    + reviewed:yes AND modified:[current TO *] +
    +
    + Lists all entries that were last modified between January and March 2012.
    + Lists all UniProtKB/Swiss-Prot entries modified in the last release. +
    name + + name:"prion protein" + + + Lists all entries for prion proteins. +
    organelle + + organelle:Mitochondrion + + + Lists all entries for proteins encoded by a gene of the mitochondrial + chromosome. +
    organism + + organism:"Ovis aries" +
    + organism:9940 +
    + organism:sheep +
    +
    +
    + Lists all entries for proteins expressed in sheep (first 2 examples) and + organisms whose name contains the term "sheep". +
    plasmid + + plasmid:ColE1 + + + Lists all entries for proteins encoded by a gene of plasmid ColE1. +
    proteome + + proteome:UP000005640 + + + Lists all entries from the human proteome. +
    proteomecomponent + + proteomecomponent:"chromosome 1" and organism:9606 + + + Lists all entries from the human chromosome 1. +
    replaces + + replaces:P02023 + + + Lists all entries that were created from a merge with entry P02023. +
    reviewed + + reviewed:yes + + + Lists all UniProtKB/Swiss-Prot entries. +
    scope + + scope:mutagenesis + + + Lists all entries containing a reference that was used to gather + information about mutagenesis. +
    sequence + + sequence:P05067-9 + + + Lists all entries containing a link to isoform 9 of the sequence + described in entry P05067. Allows searching by specific sequence + identifier. +
    sequence_modified + + sequence_modified:[20120101 TO 20120301]
    + reviewed:yes AND sequence_modified:[current TO *] +
    +
    + Lists all entries whose sequences were last modified between January and March 2012.
    + Lists all UniProtKB/Swiss-Prot entries whose sequences were modified in the last release. +
    source + + source:intact + + + Lists all entries containing a GO term whose annotation source is the + IntAct database. +
    strain + + strain:wistar + + + Lists all entries containing a reference relevant to strain wistar. +
    taxonomy + + taxonomy:40674 + + + 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). +
    tissue + + tissue:liver + + + Lists all entries containing a reference describing the protein sequence + obtained from a clone isolated from liver. +
    web + + web:wikipedia + + + Lists all entries for proteins that are described in Wikipedia. +
    + + + \ No newline at end of file diff --git a/help/html/features/uniprotseqfetcher.png b/help/html/features/uniprotseqfetcher.png new file mode 100644 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 index 0000000..ed7cbb7 --- /dev/null +++ b/help/html/features/uniprotsequencefetcher.html @@ -0,0 +1,169 @@ + + + +The UniProt Free Text Search Interface + + + + The UniProt Free Text Search Interface +
    Since version 2.10 (September 2016), the Jalview Desktop + provides a search interface for interactive discovery and retrieval of + sequence data from UniProt. This dialog enables UniProt sequence + metadata to be searched with free text and structured queries, which + allows sequences to be located via gene name, keywords, or even + via manual cross-referencing from UniProt or other + bioinformatics websites. +

    + To open the UniProt Sequence Fetcher, select UniProt as the database + from any Sequence Fetcher dialog (opened + via "File →Fetch + Sequences"). +

    +

    + UniProt sequence fetcher (introduced in Jalview 2.10) +

    + +

    + Searching the UniProt Database +

    +

    + To search UniProt, simply begin typing in the text box. After a + short delay (uabout 1.5 seconds), results will be shown in the table + below. You can sort results by clicking on the displayed columns, + and select entries with the mouse or keyboard. Once you have + selected one or more entries, hit the OK button to + retrieve the sequences. +

    +
      +
    • Searching a specific UniProt field To + find sequences with particular UniProt metadata, you can select a + field to search from the drop-down menu.
    • + + +
    • Bulk UniProt record retrieval
      To + retrieve several uniprot accessions at once, first select UniProt + ID from the dropdown menu, then paste in the accession IDs as a + semi-colon separated list. (e.g. fila_human; mnt_human; + mnt_mouse).
      Hitting Return or OK will automatically fetch + those IDs, like the default Sequence Fetcher interface.
    • + +
    • Complex queries with the UniProt query + Syntax The text box also allows complex queries to be entered. + The table below provides a brief overview of the supported syntax + (see query fields for + UniProtKB): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      human antigenAll entries containing both terms.
      human AND antigen
      human && antigen
      "human antigen"All entries containing both terms in the exact order.
      human -antigenAll entries containing the term human + but not antigen. +
      human NOT antigen
      human ! antigen
      human OR mouseAll entries containing either term.
      human || mouse
      antigen AND (human OR mouse)Using parentheses to override boolean precedence + rules.
      anti*All entries containing terms starting with anti. + Asterisks can also be used at the beginning and within + terms. Note: Terms starting with an + asterisk or a single letter followed by an asterisk can slow + down queries considerably. +
      author:Tiger*Citations that have an author whose name starts with + Tiger. 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). +
      length:[100 TO *]All entries with a sequence of at least 100 amino + acids.
      citation:(author:Arai author:Chung)All entries with a publication that was coauthored by + two specific authors.
    • +
    +

    + Result pagination +

    + The query results returned from the UniProt server are paginated for + performance optimisation. The button labelled + ' << ' and + ' >> ' 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. + + +

    + Customising The UniProt Sequence Fetcher +

    +

    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.

    +

    + The UniProt Free Test Search Interface was introduced in + Jalview 2.10.0 +

    + + \ No newline at end of file diff --git a/help/html/features/varna.html b/help/html/features/varna.html index 029edce..ce446f3 100644 --- a/help/html/features/varna.html +++ b/help/html/features/varna.html @@ -27,7 +27,7 @@ The VARNA RNA Viewer

    - VARNA was integrated + VARNA was integrated into Jalview 2.8 to allow interactive viewing of RNA secondary structure annotation. It is opened by selecting the "Structure→View Structure:" option in the sequence diff --git a/help/html/features/viewingpdbs.html b/help/html/features/viewingpdbs.html index 4d35516..edb3762 100755 --- a/help/html/features/viewingpdbs.html +++ b/help/html/features/viewingpdbs.html @@ -24,10 +24,10 @@

    - Viewing PDB Structures + Discovering and Viewing PDB Structures

    - Jalview can be used to view protein structures by following the steps - below: + Jalview can be used to explore the 3D structures of sequences in an + alignment by following the steps below:
    1. Select the "3D Structure Data..." option from a sequence's pop-up @@ -40,18 +40,18 @@ pane.
    2. However, if no structure was found, the Structure Chooser interface will present options for manual - association of PDB structures. + href="structurechooser.html">Structure Chooser interface + will present options for manual association of PDB structures.
    3. -
    4. Selecting Structures
      If structures - have been discovered, then some will already be selected according - to predefined selection criteria, such as structures with the - highest resolution. Use the drop down menu to select structures - according to different criteria, or, alternatively, choose - structures manually by selecting with the keyboard and mouse. +
    5. Selecting Structures
      You can select + the structures that you want to open and view by selecting them with + the mouse and keyboard.
      By default, if structures were + discovered, then some will already be selected according to the + criteria shown in the drop-down menu. The default criteria is + 'highest resolution', simply choose another to pick structures in + a different way.
      • Viewing Cached Structures
        If you have previously downloaded structures for your sequences, they @@ -89,9 +89,8 @@
      • If another structure is already shown for the current alignment, then you will be asked if you want to add and align this structure to the structure in the existing view. (new - feature in Jalview 2.6). + href="jmol.html#align">align this structure to the structure + in the existing view. (new feature in Jalview 2.6).
      • If the structure is already shown, then you will be @@ -107,17 +106,18 @@

        - Importing PDB Entries or files in PDB format
        - You can retrieve sequences from the PDB using the Sequence Fetcher. Any sequences retrieved with this service are - automatically associated with their source database entry. For PDB - sequences, simply select PDB as the database and enter your known - PDB id (appended with ':' and a chain code, if desired).
        - Jalview will also read PDB files directly. Simply load in the file - as you would an alignment file. The sequences of any protein or - nucleotide chains will be extracted from the file and viewed in the - alignment window. + Retrieving sequences from the PDB
        You can + retrieve sequences from the PDB using the Sequence Fetcher. The sequences + retrieved with this service are derived directly from the PDB 3D + structure data, which can be viewed in the same way above. Secondary + structure and temperature factor annotation can also be added.
        + +
        Jalview + will also read PDB files directly - either in PDB format, or mmCIF. Simply load in the file as you would + an alignment file. The sequences of any protein or nucleotide chains + will be extracted from the file and viewed in the alignment window.

        @@ -129,8 +129,8 @@ desktop, Jalview will give you the option of associating PDB files with sequences that have the same filename. This means, for example, you can automatically associate PDB files with names like '1gaq.pdb' - with sequences that have an ID like '1gaq'.
        - Note: This feature was added in Jalview 2.7 + with sequences that have an ID like '1gaq'.
        Note: + This feature was added in Jalview 2.7

        Note for Jalview applet users:
        Due to the applet @@ -150,16 +150,31 @@ Features"
        menu item and the Feature Settings dialog box.

        +
        +
        +

        + Switching between mmCIF and PDB format for + downloading files from the PDB
        Jalview now employs the mmCIF format for importing 3D structure data + from flat file and EMBL-PDBe web-service, as recommended by the + wwwPDB. If you prefer (for any reason) to download data as PDB files + instead, then first close Jalview, and add the following line to + your .jalview_properties file:
        + PDB_DOWNLOAD_FORMAT=PDB +
        When this setting is configured, Jalview will only request + PDB format files from EMBL-EBI's PDBe.
        mmCIF format + file support was added in Jalview 2.10. +

        Outstanding problem with cut'n'pasted - files in Jalview 2.6 and Jalview 2.7
        Structures - imported via the cut'n'paste dialog box will not be correctly - highlighted or coloured when they are displayed in structure - views, especially if they contain more than one PDB structure. See - the bug report at http://issues.jalview.org/browse/JAL-623 for - news on this problem.
        + files in Jalview 2.6 and Jalview 2.7
        Structures imported + via the cut'n'paste dialog box will not be correctly highlighted + or coloured when they are displayed in structure views, especially + if they contain more than one PDB structure. See the bug report at + http://issues.jalview.org/browse/JAL-623 for news on this problem.

        + diff --git a/help/html/io/index.html b/help/html/io/index.html index c0a89cf2..d2196c1 100755 --- a/help/html/io/index.html +++ b/help/html/io/index.html @@ -33,10 +33,6 @@ NBRF/PIR (including MODELLER variant), Pfam/Stockholm

        - The EBI has examples - of these file formats. -

        -

        Additionally, whole sets of coloured and annotated alignments and trees can be read from a Jalview (jar) format file using Desktop→Load diff --git a/help/html/keys.html b/help/html/keys.html index a477457..f129551 100755 --- a/help/html/keys.html +++ b/help/html/keys.html @@ -159,7 +159,7 @@

    Both Cuts the (fully) selected sequences from the alignment. +columns are selected, you should use the Hide Regions feature instead.-->
    - 2.9.1
    1/6/2016
    + 2.10.0
    20/9/2016
    General
      -
    • +
    • Jmol now primary parser for importing structure data to Jalview. Enables mmCIF and better PDB parsing.
    • +
    • Alignment ruler shows positions relative to reference sequence
    • +
    • Position/residue shown in status bar when mousing over sequence associated annotation
    Application
      -
    • +
    • +
    • +
    • +
    • +
    • +
    • +
    • Show residue labels in Chimera when mousing over sequences in Jalview
    • +
    • Support for reverse-complement coding regions in ENA and EMBL
    • +
    • Upgrade to EMBL XML 1.2 for ENA record retrieval
    • +
    • New 'execute Groovy script' option in an alignment window's Calculate menu
    • +
    • Allow groovy scripts that call Jalview.getAlignFrames() to run in headless mode
    • +
    • Store/restore reference sequence in Jalview projects
    • +
    • Chain codes for a sequence's PDB associations are now saved/restored from project
    • +
    • Double click on an entry in Jalview's database chooser opens a sequence fetcher
    • +
    • Free-text search client for UniProt using the UniProt web API
    • + +
    Applet
      -
    • +
    General
      -
    • +
    • reinstate CTRL-click for opening pop-up menu on OSX
    • +
    • Export features in Jalview format (again) includes graduated colourschemes
    • +
    • More responsive when working with big alignments and lots of hidden columns
    • +
    • hidden column markers not always rendered at right of alignment window
    • +
    • Tidied up links in help file table of contents
    • +
    • Feature based tree calculation not shown for DNA alignments
    • +
    • Hidden columns ignored during feature based tree calculation
    • +
    • Alignment view stops updating when show unconserved enabled for group on alignment
    • +
    • Cannot insert gaps into sequence when set as reference
    • +
    • Alignment column in status incorrectly shown as "Sequence position" when mousing over annotation
    • +
    Application
      -
    • +
    • +
    • Corrupt preferences for SVG, EPS & HTML output when running on non-gb/us i18n platforms
    • +
    • URLs and links can imported by drag'n'drop on OSX webstart
    • +
    • InstallAnywhere distribution fails when launching Chimera
    • +
    • Jalview very slow to launch via webstart (also hotfix for 2.9.0b2)
    • +
    • Cannot save project when view has a reference sequence defined
    • +
    • Columns are suddenly selected in other alignments and views when revealing hidden columns
    • +
    • Hide columns not mirrored in complement view in a cDNA/Protein splitframe
    • +
    • Cannot save/restore representative sequence from project when only one sequence is represented
    • + +
    Applet
      -
    • +