From: jprocter Date: Fri, 9 Feb 2007 18:02:18 +0000 (+0000) Subject: This commit was manufactured by cvs2svn to create tag 'Release_2_1_1'. X-Git-Tag: Release_2_1_1 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=e059f46e77f5106185c5ebd6be7c5c31c2fbd230;p=jalview.git This commit was manufactured by cvs2svn to create tag 'Release_2_1_1'. Sprout from master 2006-09-12 10:01:52 UTC andrew 'Copy consensus' Cherrypick from master 2007-02-09 18:02:17 UTC jprocter 'running jalview from the command line.': doc/AddingGroovySupport.html help/html/align.jpg help/html/colourSchemes/textcolour.gif help/html/colourSchemes/textcolour.html help/html/features/amendfeature.gif help/html/features/clarguments.html help/html/features/codingfeatures.html help/html/features/commandline.html help/html/features/creatinFeatures.html help/html/features/crnewfeature.gif help/html/features/das.gif help/html/features/editingFeatures.html help/html/features/multipleViews.html help/html/features/newkeystrokes.html help/html/features/seqmappings.html help/html/menus/alwformat.html help/html/menus/alwselect.html lib/vamsas-client.jar src/jalview/analysis/Finder.java src/jalview/appletgui/EditNameDialog.java src/jalview/commands/ChangeCaseCommand.java src/jalview/commands/CommandI.java src/jalview/commands/EditCommand.java src/jalview/commands/OrderCommand.java src/jalview/commands/RemoveGapColCommand.java src/jalview/commands/RemoveGapsCommand.java src/jalview/commands/TrimRegionCommand.java src/jalview/gui/EditNameDialog.java src/jalview/gui/TextColourChooser.java src/jalview/gui/UserQuestionnaireCheck.java src/jalview/gui/VamsasClient.java src/jalview/io/AMSAFile.java src/jalview/io/VamsasDatastore.java src/jalview/schemes/ScoreMatrix.java src/jalview/util/MapList.java src/jalview/ws/JPredThread.java --- diff --git a/doc/AddingGroovySupport.html b/doc/AddingGroovySupport.html new file mode 100644 index 0000000..6726612 --- /dev/null +++ b/doc/AddingGroovySupport.html @@ -0,0 +1,122 @@ + +Adding Groovy Support to Jalview + + +

+Adding Groovy Support to Jalview +

+

+There is currently no scripting language +extension within Jalview, in part because a +scripting API has not been developed. +

+

It is, however, really easy to embed scripting +engines within Jalview. We haven't done it +with the Bean Scripting Framework, but the +code snippets below show you how to get going +with groovy. +

+

Modifications

+

+For each class below, add the following objects and methods to their definitions. +

+ +

+Finally, compile and run with the groovy-all-*.jar (get the jar +from the embedded directory within the groovy distribution). +Then, you should be able to open the Groovy shell +window from the Desktop's Tools menu. To check things are working, +try a simple test script :
+

+  
+  print Jalview.getAlignframes()[0].getTitle();
+
+Executing this will print the title of the first alignment loaded into Jalview.

+ +

TODO

+

+Using Java class methods from Groovy is straightforward, but currently, there isn't a set of easy to use methods for the jalview objects. A Jalview Scripting API needs to be developed to make this easier.

+

Making it easier

+

jalview.bin.JalviewScript could be a top level jalview instance of a script execution thread, creating and maintaining the context for scripts operating on the jalview datamodel and interfacing with the Jalview GUI. +

+ + + diff --git a/help/html/align.jpg b/help/html/align.jpg new file mode 100644 index 0000000..451ccaa Binary files /dev/null and b/help/html/align.jpg differ diff --git a/help/html/colourSchemes/textcolour.gif b/help/html/colourSchemes/textcolour.gif new file mode 100644 index 0000000..a659156 Binary files /dev/null and b/help/html/colourSchemes/textcolour.gif differ diff --git a/help/html/colourSchemes/textcolour.html b/help/html/colourSchemes/textcolour.html new file mode 100644 index 0000000..f40116a --- /dev/null +++ b/help/html/colourSchemes/textcolour.html @@ -0,0 +1,20 @@ + + +Background Dependent Text Colour + + +Background Dependent Text Colour +

The Colour→Text Colour menu entry opens +the "Adjust Foreground Text Colour Threshold" +dialog box, allowing the colour of symbols rendered on dark or light +backgrounds to be set for the current selection or the whole alignment. +

+

+

The dialog box contains a slider, and two colour icons showing +the text colour for dark backgrounds (left hand end of slider), and +light backgrounds (right hand end of slider). Drag the slider to change +the threshold for transitioning between dark and light background +colours, and select either of the colour boxes to open a colour chooser +to select a different text colour.

+ + diff --git a/help/html/features/amendfeature.gif b/help/html/features/amendfeature.gif new file mode 100644 index 0000000..39cc53a Binary files /dev/null and b/help/html/features/amendfeature.gif differ diff --git a/help/html/features/clarguments.html b/help/html/features/clarguments.html new file mode 100644 index 0000000..45dfab5 --- /dev/null +++ b/help/html/features/clarguments.html @@ -0,0 +1,82 @@ + +Jalview Command Line Arguments + + +

The Jalview Executable's Command Line + Arguments

+ See running Jalview from the command + line for more information.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-nodisplay
Run Jalview without User Interface.
-props FILE
Use the given Jalview properties file instead + of users default.
-features FILE
+

Use the given file to add sequence features to an alignment. + See Features + File (Known as Groups file prior to 2.08) description.

+ +
-annotations FILE
Add precalculated annotations to the alignment. See Annotation + File description.
-fasta FILE
Create alignment file FILE in Fasta format.
-clustal FILE
Create alignment file FILE in Clustal format.
-msf FILE
Create alignment file FILE in MSF format.
-pileup FILE
Create alignment file FILE in Pileup format.
-pir FILE
Create alignment file FILE in PIR format.
-blc FILE
Create alignment file FILE in BLC format.
-jalview FILE
Create alignment file FILE in Jalview format.
-png FILE
Create PNG image FILE from alignment.
-imgMap FILE
Create HTML file FILE with image map of PNG + image.
-eps FILE
Create EPS file FILE from alignment.
+ + diff --git a/help/html/features/codingfeatures.html b/help/html/features/codingfeatures.html new file mode 100644 index 0000000..a828545 --- /dev/null +++ b/help/html/features/codingfeatures.html @@ -0,0 +1,16 @@ + + +DNA Sequence Coding Region Definition + + +

DNA Sequence Coding Region Definition

+

Jalview includes the standard DNA codon translation table in +order to be able to dynamically translate cDNA to its expressed +protein sequence. DNA Sequence Coding Regions are sequence +features that can be defined on any DNA sequence in order to +mark stretches of cDNA that will be concatenated to form the +series of codons that are translated by the " +Calculate→Translate cDNA" menu function. +

+ + \ No newline at end of file diff --git a/help/html/features/commandline.html b/help/html/features/commandline.html new file mode 100644 index 0000000..fadddce --- /dev/null +++ b/help/html/features/commandline.html @@ -0,0 +1,35 @@ + +Running Jalview from the command line + +

Running Jalview from the command line

+

Jalview is most easily run from the command line if you have built + it from source, or via the 'Jalview' executable created from the + InstallAnywhere jalview installation. Both of these mechanisms allow + true command line execution of jalview - allowing you to provide + additional options.

+The Java Webstart version of + jalview can be executed from the command line using something like + : +

javaws http://www.jalview.org/webstart/jalview.jnlp -open
+ yourFileName
+ But, this is not guaranteed to work on all versions of webstart on all + operating systems, and doesn't let you execute Jalview with any + additional parameters. +

Running jalview from the InstallAnywhere + installation

+

If you install with InstallAnywhere you can use several more commands. + However, if you call the application with the link provided by InstallAnywhere + any output from the application will be sent to output.txt, not standard + out.
+ The jalview application also requires a number of additional + libraries on the class path. The command line below adds the Jalview + installation's 'lib' directory to the list of directories that are + searched for jars to be added to the classpath:

+
java -Djava.ext.dirs=$INSTALL_DIR$/lib -cp $INSTALL_DIR$/jalview.jar jalview.bin.Jalview -open [FILE] 
+

Use '-help' to get more information on the command line arguments that Jalview + accepts.

+

 

+

 

+ + diff --git a/help/html/features/creatinFeatures.html b/help/html/features/creatinFeatures.html new file mode 100644 index 0000000..0ebb52f --- /dev/null +++ b/help/html/features/creatinFeatures.html @@ -0,0 +1,38 @@ + + +Creating Sequence Features + + +Creating Sequence Features +

Jalview can create sequence features from the matches of a regular expression search, or from the currently +selected area via the "selection→Create +sequence feature" entry in the selection area popup menu. In both +cases, the Create Features dialog box will then be +opened:

+

+

Select or enter the attributes for the features being created, +and then press OK to create the new features.

+

Each attribute is described below: +

+

Sequence Feature Creation was introduced in Jalview Version 2.2

+ + \ No newline at end of file diff --git a/help/html/features/crnewfeature.gif b/help/html/features/crnewfeature.gif new file mode 100644 index 0000000..5aa6edb Binary files /dev/null and b/help/html/features/crnewfeature.gif differ diff --git a/help/html/features/das.gif b/help/html/features/das.gif new file mode 100644 index 0000000..1f884cb Binary files /dev/null and b/help/html/features/das.gif differ diff --git a/help/html/features/editingFeatures.html b/help/html/features/editingFeatures.html new file mode 100644 index 0000000..ff4d5ca --- /dev/null +++ b/help/html/features/editingFeatures.html @@ -0,0 +1,26 @@ + + +Amending or Deleting Sequence Features + + +Amending or Deleting Sequence Features +

Double clicking a position in the alignment with one or more +displayed sequence features opens the "Amend/Delete +Features" dialog box.

+

+

The dialog box only allows one of the features at the +double-clicked position to be edited or deleted at a time, and it will +also be highlighted in black in the alignment window.

+

Choose which feature is to be modified by selecting it from the Sequence +Feature Name pull down +menu. In addition to the Name, group, colour and description attributes +described for the new feature dialog +box, a feature's start and end position can be changed either by +entering a new position directly or by using the adjacent up and down +buttons.

+

Select Amend to update the feature, Delete +to remove the selected feature, or Cancel to leave the +feature unchanged.

+

Sequence feature editing was implemented in Jalview 2.2

+ + diff --git a/help/html/features/multipleViews.html b/help/html/features/multipleViews.html new file mode 100644 index 0000000..da811d4 --- /dev/null +++ b/help/html/features/multipleViews.html @@ -0,0 +1,58 @@ + + +Multiple Alignment Views + + +

Multiple Alignment Views

+

Multiple alignment views allows the same alignment to be viewed +independently in many different ways simultaneously. Each view is an +independent visualization of the same alignment, so each may have a +different ordering, colouring, row and column hiding and seuqence +feature and annotation display setting, but alignment, feature and +annotation edits are common to all, since this affects the underlying +data.

+

Create a new view using the "View→New +View" menu item, or by pressing Control+T. A newly +created view will be identical to the view it was created from, but any +changes to the way the alignment is coloured or displayed will only +affect the new view.

+

A particular view may focus on some specific aspect of an +alignment - for example, hiding all but the region of an alignment +containing a particular domain. Right-clicking a view's +tab opens the View Name dialog box, allowing it to be renamed to +something more meaningful.

+

Viewing Multiple Views Simultaneously

+

Multiple views of an alignment are, by default, gathered together +as tabs within a single alignment window. They can be viewed +simultanously by pressing X (or via "View→Expand") +to expand each view into its own linked alignment window. Expanded views +are gathered back into into a single tabbed alignment window by pressing +G, or by selecting "View→Gather"). +

+

Hidden Sequence Representatives and Multiple +Views

+

There are some unexpected interactions between hidden sequence +representatives and their display in multiple views. See the +corresponding entry in the documentation +for hidden regions.

+

Structure and Analysis Viewers and Multiple +Views

+

A tree calculated on a particular view, or loaded onto it, is by +default associated with just that view. However, the Tree Viewer's "View→Associate +leaves" submenu allows a tree's view association to be changed to +to any or all other views.

+

The results of a PCA +calculation on a particular view may also be associated with other +views, using the PCA Viewer's "View→Associate +Nodes" submenu.

+

Currently, a PDB Structure Viewer +opened on a structure associated with a sequence in a particular view +will only be associated with the seuqence as displayed in that view.
+This will be resolved in a future release +

Multiple Views were introduced in Jalview 2.2

+ + diff --git a/help/html/features/newkeystrokes.html b/help/html/features/newkeystrokes.html new file mode 100644 index 0000000..12ea967 --- /dev/null +++ b/help/html/features/newkeystrokes.html @@ -0,0 +1,33 @@ + +New Key Strokes and Menus + +New Key Strokes and Menus +

Many new keyboard shortcuts have been +added in Jalview 2.2 to make editing, selecting and navigating an +alignment even easier. The selection commands in the Edit +menu, and the alignment formatting controls within the View +menu have also been moved into their own respective Select +and Format menus.

+

Some of the more important new keystrokes are shown below : +

+

+ + diff --git a/help/html/features/seqmappings.html b/help/html/features/seqmappings.html new file mode 100644 index 0000000..46fcd59 --- /dev/null +++ b/help/html/features/seqmappings.html @@ -0,0 +1,22 @@ + + +Mapping Between Different Sequences + + +

Mapping Between Different Sequences

+

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

+

The most familiar mapping is the one used to identify +the coordinates corresponding to a displayed sequence when +viewing a PDB file associated with a sequence (see +"Viewing PDB Files" +for more information.

+

The newest form of mapping supported by Jalview is the +correspondence between DNA and protein sequences. This mapping +can be imported directly from EMBL and EMBLCDS database records +retrieved by the Sequence Fetcher, +or by the definition of coding regions. + + \ No newline at end of file diff --git a/help/html/menus/alwformat.html b/help/html/menus/alwformat.html new file mode 100644 index 0000000..d7224b3 --- /dev/null +++ b/help/html/menus/alwformat.html @@ -0,0 +1,56 @@ + + +

Alignment Window Format Menu

+ + + diff --git a/help/html/menus/alwselect.html b/help/html/menus/alwselect.html new file mode 100644 index 0000000..34438cd --- /dev/null +++ b/help/html/menus/alwselect.html @@ -0,0 +1,29 @@ + + +

Alignment Window Select Menu

+ + + diff --git a/lib/vamsas-client.jar b/lib/vamsas-client.jar new file mode 100644 index 0000000..e0c7d95 Binary files /dev/null and b/lib/vamsas-client.jar differ diff --git a/src/jalview/analysis/Finder.java b/src/jalview/analysis/Finder.java new file mode 100644 index 0000000..2fa09aa --- /dev/null +++ b/src/jalview/analysis/Finder.java @@ -0,0 +1,308 @@ +package jalview.analysis; + +import java.util.*; + +import jalview.datamodel.*; + +public class Finder +{ + /** + * Implements the search algorithms for the Find dialog box. + */ + SearchResults searchResults; + AlignmentI alignment; + jalview.datamodel.SequenceGroup selection = null; + Vector idMatch = null; + boolean caseSensitive = false; + boolean findAll = false; + com.stevesoft.pat.Regex regex = null; + /** + * hold's last-searched position between calles to find(false) + */ + int seqIndex = 0, resIndex = 0; + public Finder(AlignmentI alignment, SequenceGroup selection) + { + this.alignment = alignment; + this.selection = selection; + } + + public Finder(AlignmentI alignment, SequenceGroup selectionGroup, + int seqIndex, int resIndex) + { + this(alignment, selectionGroup); + this.seqIndex = seqIndex; + this.resIndex = resIndex; + } + + public boolean find(String searchString) + { + boolean hasResults = false; + if (!caseSensitive) + { + searchString = searchString.toUpperCase(); + } + regex = new com.stevesoft.pat.Regex(searchString); + searchResults = new SearchResults(); + idMatch = new Vector(); + Sequence seq; + String item = null; + boolean found = false; + + ////// is the searchString a residue number? + try + { + int res = Integer.parseInt(searchString); + found = true; + if (selection == null || selection.getSize() < 1) + { + seq = (Sequence) alignment.getSequenceAt(0); + } + else + { + seq = (Sequence) (selection.getSequenceAt(0)); + } + + searchResults.addResult(seq, res, res); + hasResults = true; + } + catch (NumberFormatException ex) + { + } + + /////////////////////////////////////////////// + + int end = alignment.getHeight(); + + if (selection != null) + { + if ( (selection.getSize() < 1) || + ( (selection.getEndRes() - selection.getStartRes()) < 2)) + { + selection = null; + } + } + + while (!found && (seqIndex < end)) + { + seq = (Sequence) alignment.getSequenceAt(seqIndex); + + if ( (selection != null) && !selection.getSequences(null).contains(seq)) + { + seqIndex++; + resIndex = 0; + + continue; + } + + item = seq.getSequenceAsString(); + // JBPNote - check if this toUpper which is present in the application implementation makes a difference + //if(!caseSensitive) + // item = item.toUpperCase(); + + if ( (selection != null) && + (selection.getEndRes() < alignment.getWidth() - 1)) + { + item = item.substring(0, selection.getEndRes() + 1); + } + + ///Shall we ignore gaps???? - JBPNote: Add Flag for forcing this or not + StringBuffer noGapsSB = new StringBuffer(); + int insertCount = 0; + Vector spaces = new Vector(); + + for (int j = 0; j < item.length(); j++) + { + if (!jalview.util.Comparison.isGap(item.charAt(j))) + { + noGapsSB.append(item.charAt(j)); + spaces.addElement(new Integer(insertCount)); + } + else + { + insertCount++; + } + } + + String noGaps = noGapsSB.toString(); + + for (int r = resIndex; r < noGaps.length(); r++) + { + + if (regex.searchFrom(noGaps, r)) + { + resIndex = regex.matchedFrom(); + + if ( (selection != null) && + ( (resIndex + + Integer.parseInt(spaces.elementAt(resIndex).toString())) < + selection.getStartRes())) + { + continue; + } + + int sres = seq.findPosition(resIndex + + Integer.parseInt(spaces.elementAt( + resIndex) + .toString())); + int eres = seq.findPosition(regex.matchedTo() - 1 + + Integer.parseInt(spaces.elementAt(regex. + matchedTo() - + 1).toString())); + + searchResults.addResult(seq, sres, eres); + hasResults = true; + if (!findAll) + { + // thats enough, break and display the result + found = true; + resIndex++; + + break; + } + + r = resIndex; + } + else + { + break; + } + } + + if (!found) + { + seqIndex++; + resIndex = 0; + } + } + + for (int id = 0; id < alignment.getHeight(); id++) + { + if (regex.search(alignment.getSequenceAt(id).getName())) + { + idMatch.addElement(alignment.getSequenceAt(id)); + hasResults = true; + } + } + return hasResults; + } + + /** + * @return the alignment + */ + public AlignmentI getAlignment() + { + return alignment; + } + + /** + * @param alignment the alignment to set + */ + public void setAlignment(AlignmentI alignment) + { + this.alignment = alignment; + } + + /** + * @return the caseSensitive + */ + public boolean isCaseSensitive() + { + return caseSensitive; + } + + /** + * @param caseSensitive the caseSensitive to set + */ + public void setCaseSensitive(boolean caseSensitive) + { + this.caseSensitive = caseSensitive; + } + + /** + * @return the findAll + */ + public boolean isFindAll() + { + return findAll; + } + + /** + * @param findAll the findAll to set + */ + public void setFindAll(boolean findAll) + { + this.findAll = findAll; + } + + /** + * @return the selection + */ + public jalview.datamodel.SequenceGroup getSelection() + { + return selection; + } + + /** + * @param selection the selection to set + */ + public void setSelection(jalview.datamodel.SequenceGroup selection) + { + this.selection = selection; + } + + /** + * @return the idMatch + */ + public Vector getIdMatch() + { + return idMatch; + } + + /** + * @return the regex + */ + public com.stevesoft.pat.Regex getRegex() + { + return regex; + } + + /** + * @return the searchResults + */ + public SearchResults getSearchResults() + { + return searchResults; + } + + /** + * @return the resIndex + */ + public int getResIndex() + { + return resIndex; + } + + /** + * @param resIndex the resIndex to set + */ + public void setResIndex(int resIndex) + { + this.resIndex = resIndex; + } + + /** + * @return the seqIndex + */ + public int getSeqIndex() + { + return seqIndex; + } + + /** + * @param seqIndex the seqIndex to set + */ + public void setSeqIndex(int seqIndex) + { + this.seqIndex = seqIndex; + } +} diff --git a/src/jalview/appletgui/EditNameDialog.java b/src/jalview/appletgui/EditNameDialog.java new file mode 100644 index 0000000..3e197f0 --- /dev/null +++ b/src/jalview/appletgui/EditNameDialog.java @@ -0,0 +1,111 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package jalview.appletgui; + +import java.awt.*; +import java.awt.event.*; + +public class EditNameDialog + extends Dialog implements ActionListener +{ + TextField id, description; + Button ok = new Button("Accept"); + Button cancel = new Button("Cancel"); + boolean accept = false; + + public String getName() + { + return id.getText(); + } + + public String getDescription() + { + if (description.getText().length() < 1) + { + return null; + } + else + { + return description.getText(); + } + } + + public EditNameDialog(String name, + String desc, + String label1, + String label2, + AlignmentPanel ap, + String title) + { + super(ap.alignFrame, title, true); + + id = new TextField(name, 40); + description = new TextField(desc, 40); + Panel panel = new Panel(new BorderLayout()); + Panel panel2 = new Panel(new BorderLayout()); + Label label = new Label(label1); + label.setFont(new Font("Monospaced", Font.PLAIN, 12)); + panel2.add(label, BorderLayout.WEST); + panel2.add(id, BorderLayout.CENTER); + panel.add(panel2, BorderLayout.NORTH); + panel2 = new Panel(new BorderLayout()); + label = new Label(label2); + label.setFont(new Font("Monospaced", Font.PLAIN, 12)); + panel2.add(label, BorderLayout.WEST); + panel2.add(description, BorderLayout.CENTER); + panel.add(panel2, BorderLayout.CENTER); + + panel2 = new Panel(new FlowLayout()); + + panel2.add(ok); + panel2.add(cancel); + ok.addActionListener(this); + cancel.addActionListener(this); + + panel.add(panel2, BorderLayout.SOUTH); + + add(panel, BorderLayout.NORTH); + + int width = 500, height = 100; + + pack(); + + height += getInsets().top + getInsets().bottom; + + setBounds(ap.alignFrame.getBounds().x + + (ap.alignFrame.getSize().width - width) / 2, + ap.alignFrame.getBounds().y + + (ap.alignFrame.getSize().height - height) / 2, + width, height); + + show(); + + } + + public void actionPerformed(ActionEvent evt) + { + if (evt.getSource() == ok) + { + accept = true; + } + + setVisible(false); + } +} diff --git a/src/jalview/commands/ChangeCaseCommand.java b/src/jalview/commands/ChangeCaseCommand.java new file mode 100644 index 0000000..d34552d --- /dev/null +++ b/src/jalview/commands/ChangeCaseCommand.java @@ -0,0 +1,133 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +package jalview.commands; + +import jalview.datamodel.*; + +public class ChangeCaseCommand + implements CommandI +{ + String description; + public static int TO_LOWER = 0; + public static int TO_UPPER = 1; + public static int TOGGLE_CASE = 2; + int caseChange = -1; + SequenceI[] seqs; + int[][] regions; + public ChangeCaseCommand(String description, + SequenceI[] seqs, + int[][] regions, + int caseChange) + { + this.description = description; + this.seqs = seqs; + this.regions = regions; + this.caseChange = caseChange; + doCommand(); + } + + public String getDescription() + { + return description; + } + + public int getSize() + { + return 1; + } + + public void doCommand() + { + changeCase(true); + } + + public void undoCommand() + { + changeCase(false); + } + + void changeCase(boolean doCommand) + { + String sequence; + int start, end; + char nextChar; + for (int r = 0; r < regions.length; r++) + { + start = regions[r][0]; + for (int s = 0; s < seqs.length; s++) + { + sequence = seqs[s].getSequenceAsString(); + StringBuffer newSeq = new StringBuffer(); + + if (regions[r][1] > sequence.length()) + { + end = sequence.length(); + } + else + { + end = regions[r][1]; + } + + if (start > 0) + { + newSeq.append(sequence.substring(0, start)); + } + + if ( (caseChange == TO_UPPER && doCommand) + || (caseChange == TO_LOWER && !doCommand)) + { + newSeq.append(sequence.substring(start, end).toUpperCase()); + } + + else if ( (caseChange == TO_LOWER && doCommand) + || (caseChange == TO_UPPER && !doCommand)) + { + newSeq.append(sequence.substring(start, end).toLowerCase()); + } + + else //TOGGLE CASE + { + for (int c = start; c < end; c++) + { + nextChar = sequence.charAt(c); + if ('a' <= nextChar && nextChar <= 'z') + { + // TO UPPERCASE !!! + nextChar -= ('a' - 'A'); + } + else if ('A' <= nextChar && nextChar <= 'Z') + { + // TO LOWERCASE !!! + nextChar += ('a' - 'A'); + } + newSeq.append(nextChar); + } + } + + if (end < sequence.length()) + { + newSeq.append(sequence.substring(end)); + } + + seqs[s].setSequence(newSeq.toString()); + } + } + } + +} diff --git a/src/jalview/commands/CommandI.java b/src/jalview/commands/CommandI.java new file mode 100644 index 0000000..d97a6bb --- /dev/null +++ b/src/jalview/commands/CommandI.java @@ -0,0 +1,30 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +package jalview.commands; + +public interface CommandI +{ + public void doCommand(); + + public void undoCommand(); + + public String getDescription(); + + public int getSize(); +} diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java new file mode 100644 index 0000000..d0a2e03 --- /dev/null +++ b/src/jalview/commands/EditCommand.java @@ -0,0 +1,650 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +package jalview.commands; + +import java.util.*; + +import jalview.datamodel.*; + +/** + * + *

Title: EditCommmand

+ * + *

Description: Essential information for performing + * undo and redo for cut/paste insert/delete gap + * which can be stored in the HistoryList

+ * + *

Copyright: Copyright (c) 2006

+ * + *

Company: Dundee University

+ * + * @author not attributable + * @version 1.0 + */ +public class EditCommand + implements CommandI +{ + public static final int INSERT_GAP = 0; + public static final int DELETE_GAP = 1; + public static final int CUT = 2; + public static final int PASTE = 3; + + Edit[] edits; + + String description; + + public EditCommand() + {} + + public EditCommand(String description) + { + this.description = description; + } + + public EditCommand(String description, + int command, + SequenceI[] seqs, + int position, + int number, + AlignmentI al) + { + this.description = description; + if (command == CUT || command == PASTE) + { + edits = new Edit[] + { + new Edit(command, seqs, position, number, al)}; + } + + performEdit(0); + } + + final public String getDescription() + { + return description; + } + + public int getSize() + { + return edits == null ? 0 : edits.length; + } + + final public AlignmentI getAlignment() + { + return edits[0].al; + } + + final public void appendEdit(int command, + SequenceI[] seqs, + int position, + int number, + AlignmentI al, + boolean performEdit) + { + Edit edit = new Edit(command, seqs, position, number, al.getGapCharacter()); + if (al.getHeight() == seqs.length) + { + edit.al = al; + edit.fullAlignmentHeight = true; + } + + if (edits != null) + { + Edit[] temp = new Edit[edits.length + 1]; + System.arraycopy(edits, 0, temp, 0, edits.length); + edits = temp; + edits[edits.length - 1] = edit; + } + else + { + edits = new Edit[] + { + edit}; + } + + if (performEdit) + { + performEdit(edits.length - 1); + } + } + + final void performEdit(int commandIndex) + { + int eSize = edits.length; + for (int e = commandIndex; e < eSize; e++) + { + if (edits[e].command == INSERT_GAP) + { + insertGap(edits[e]); + } + else if (edits[e].command == DELETE_GAP) + { + deleteGap(edits[e]); + } + else if (edits[e].command == CUT) + { + cut(edits[e]); + } + else if (edits[e].command == PASTE) + { + paste(edits[e]); + } + } + } + + final public void doCommand() + { + performEdit(0); + } + + final public void undoCommand() + { + int e = 0, eSize = edits.length; + for (e = eSize - 1; e > -1; e--) + { + if (edits[e].command == INSERT_GAP) + { + deleteGap(edits[e]); + } + else if (edits[e].command == DELETE_GAP) + { + insertGap(edits[e]); + } + else if (edits[e].command == CUT) + { + paste(edits[e]); + } + else if (edits[e].command == PASTE) + { + cut(edits[e]); + } + } + } + + final void insertGap(Edit command) + { + for (int s = 0; s < command.seqs.length; s++) + { + command.seqs[s].insertCharAt(command.position, + command.number, + command.gapChar); + } + + adjustAnnotations(command, true); + } + + final void deleteGap(Edit command) + { + for (int s = 0; s < command.seqs.length; s++) + { + command.seqs[s].deleteChars(command.position, + command.position + command.number); + } + + adjustAnnotations(command, false); + } + + void cut(Edit command) + { + command.string = new char[command.seqs.length][]; + + for (int i = 0; i < command.seqs.length; i++) + { + if (command.seqs[i].getLength() > command.position) + { + command.string[i] = command.seqs[i].getSequence(command.position, + command.position + command.number); + + if (command.seqs[i].getDatasetSequence() != null + || command.seqs[i].getSequenceFeatures() != null) + { + for (int s = command.position; s < command.position + command.number; + s++) + { + if (jalview.schemes.ResidueProperties + .aaIndex[command.seqs[i].getCharAt(s)] != 23) + { + adjustFeatures(command, i, + command.seqs[i].findPosition(command.position), + command.seqs[i].findPosition(command.position + + command.number), + false); + break; + } + } + } + command.seqs[i].deleteChars(command.position, + command.position + command.number); + } + + if (command.seqs[i].getLength() < 1) + { + command.al.deleteSequence(command.seqs[i]); + } + } + + adjustAnnotations(command, false); + } + + void paste(Edit command) + { + StringBuffer tmp; + boolean newDSNeeded; + int start = 0, end = 0; + + for (int i = 0; i < command.seqs.length; i++) + { + newDSNeeded = false; + if (command.seqs[i].getLength() < 1) + { + // ie this sequence was deleted, we need to + // read it to the alignment + if (command.alIndex[i] < command.al.getHeight()) + { + command.al.getSequences().insertElementAt(command.seqs[i], + command.alIndex[i]); + } + else + { + command.al.addSequence(command.seqs[i]); + } + } + tmp = new StringBuffer(); + tmp.append(command.seqs[i].getSequence()); + + if (command.string != null && command.string[i] != null) + { + if (command.position >= tmp.length()) + { + //This occurs if padding is on, and residues + //are removed from end of alignment + int length = command.position - tmp.length(); + while (length > 0) + { + tmp.append(command.gapChar); + length--; + } + } + tmp.insert(command.position, command.string[i]); + + for (int s = 0; s < command.string[i].length; s++) + { + if (jalview.schemes.ResidueProperties.aaIndex[command.string[i][s]] != + 23) + { + newDSNeeded = true; + start = command.seqs[i].findPosition(command.position); + end = command.seqs[i].findPosition(command.position + + command.number); + break; + } + } + command.string[i] = null; + } + + command.seqs[i].setSequence(tmp.toString()); + + if (newDSNeeded) + { + if (command.seqs[i].getDatasetSequence() != null) + { + Sequence ds = new Sequence(command.seqs[i].getName(), + jalview.analysis.AlignSeq.extractGaps( + jalview.util.Comparison.GapChars, + command.seqs[i].getSequenceAsString() + ), + command.seqs[i].getStart(), + command.seqs[i].getEnd()); + ds.setDescription(command.seqs[i].getDescription()); + command.seqs[i].setDatasetSequence(ds); + } + + adjustFeatures(command, i, start, end, true); + } + } + + adjustAnnotations(command, true); + + command.string = null; + } + + final void adjustAnnotations(Edit command, boolean insert) + { + + AlignmentAnnotation[] annotations = null; + + if (command.fullAlignmentHeight) + { + annotations = command.al.getAlignmentAnnotation(); + } + else + { + int aSize = 0; + AlignmentAnnotation[] tmp; + for (int s = 0; s < command.seqs.length; s++) + { + if (command.seqs[s].getAnnotation() == null) + { + continue; + } + + if (aSize == 0) + { + annotations = command.seqs[s].getAnnotation(); + } + else + { + tmp = new AlignmentAnnotation + [aSize + command.seqs[s].getAnnotation().length]; + + System.arraycopy(annotations, 0, tmp, 0, aSize); + + System.arraycopy(command.seqs[s].getAnnotation(), + 0, tmp, aSize, + command.seqs[s].getAnnotation().length); + + annotations = tmp; + } + + aSize = annotations.length; + } + } + + if (annotations == null) + { + return; + } + + if (!insert) + { + command.deletedAnnotations = new Hashtable(); + } + + int aSize; + Annotation[] temp; + for (int a = 0; a < annotations.length; a++) + { + if (annotations[a].autoCalculated) + { + continue; + } + + int tSize = 0; + + aSize = annotations[a].annotations.length; + if (insert) + { + temp = new Annotation[aSize + command.number]; + } + else + { + if (command.position < aSize) + { + if (command.position + command.number > aSize) + { + tSize = aSize; + } + else + { + tSize = aSize - command.number + command.position; + } + } + else + { + tSize = aSize; + } + + if (tSize < 0) + { + tSize = aSize; + } + temp = new Annotation[tSize]; + + } + + if (insert) + { + if (command.position < annotations[a].annotations.length) + { + System.arraycopy(annotations[a].annotations, + 0, temp, 0, command.position); + + if (command.deletedAnnotations != null + && + command.deletedAnnotations.containsKey(annotations[a]. + annotationId)) + { + Annotation[] restore = (Annotation[]) + command.deletedAnnotations.get(annotations[a].annotationId); + + System.arraycopy(restore, + 0, + temp, + command.position, + command.number); + + } + + System.arraycopy(annotations[a].annotations, + command.position, temp, + command.position + command.number, + aSize - command.position); + } + else + { + if (command.deletedAnnotations != null + && + command.deletedAnnotations.containsKey(annotations[a]. + annotationId)) + { + Annotation[] restore = (Annotation[]) + command.deletedAnnotations.get(annotations[a].annotationId); + + temp = new Annotation[annotations[a].annotations.length + + restore.length]; + System.arraycopy(annotations[a].annotations, + 0, temp, 0, + annotations[a].annotations.length); + System.arraycopy(restore, 0, temp, + annotations[a].annotations.length, restore.length); + } + else + { + temp = annotations[a].annotations; + } + } + } + else + { + if (tSize != aSize || command.position < 2) + { + System.arraycopy(annotations[a].annotations, + 0, temp, 0, command.position); + + Annotation[] deleted = new Annotation[command.number]; + System.arraycopy(annotations[a].annotations, + command.position, deleted, 0, command.number); + + command.deletedAnnotations.put(annotations[a].annotationId, + deleted); + + System.arraycopy(annotations[a].annotations, + command.position + command.number, + temp, command.position, + aSize - command.position - command.number); + } + else + { + int dSize = aSize - command.position; + + if (dSize > 0) + { + Annotation[] deleted = new Annotation[command.number]; + System.arraycopy(annotations[a].annotations, + command.position, deleted, 0, dSize); + + command.deletedAnnotations.put(annotations[a].annotationId, + deleted); + + tSize = Math.min(annotations[a].annotations.length, + command.position); + temp = new Annotation[tSize]; + System.arraycopy(annotations[a].annotations, + 0, temp, 0, tSize); + } + else + { + temp = annotations[a].annotations; + } + } + } + + annotations[a].annotations = temp; + } + } + + final void adjustFeatures(Edit command, int index, int i, int j, + boolean insert) + { + SequenceI seq = command.seqs[index]; + SequenceI sequence = seq.getDatasetSequence(); + if (sequence == null) + { + sequence = seq; + } + + if (insert) + { + if (command.editedFeatures != null + && command.editedFeatures.containsKey(seq)) + { + sequence.setSequenceFeatures( + (SequenceFeature[]) command.editedFeatures.get(seq) + ); + } + + return; + } + + SequenceFeature[] sf = sequence.getSequenceFeatures(); + + if (sf == null) + { + return; + } + + SequenceFeature[] oldsf = new SequenceFeature[sf.length]; + + int cSize = j - i; + + for (int s = 0; s < sf.length; s++) + { + SequenceFeature copy = new SequenceFeature(sf[s]); + + oldsf[s] = copy; + + if (sf[s].getEnd() < i) + { + continue; + } + + if (sf[s].getBegin() > j) + { + sf[s].setBegin(copy.getBegin() - cSize); + sf[s].setEnd(copy.getEnd() - cSize); + continue; + } + + if (sf[s].getBegin() >= i) + { + sf[s].setBegin(i); + } + + if (sf[s].getEnd() < j) + { + sf[s].setEnd(j - 1); + } + + sf[s].setEnd(sf[s].getEnd() - (cSize)); + + if (sf[s].getBegin() > sf[s].getEnd()) + { + sequence.deleteFeature(sf[s]); + } + } + + if (command.editedFeatures == null) + { + command.editedFeatures = new Hashtable(); + } + + command.editedFeatures.put(seq, oldsf); + + } + + class Edit + { + boolean fullAlignmentHeight = false; + Hashtable deletedAnnotations; + Hashtable editedFeatures; + AlignmentI al; + int command; + char[][] string; + SequenceI[] seqs; + int[] alIndex; + int position, number; + char gapChar; + + Edit(int command, + SequenceI[] seqs, + int position, + int number, + char gapChar) + { + this.command = command; + this.seqs = seqs; + this.position = position; + this.number = number; + this.gapChar = gapChar; + } + + Edit(int command, + SequenceI[] seqs, + int position, + int number, + AlignmentI al) + { + this.gapChar = al.getGapCharacter(); + this.command = command; + this.seqs = seqs; + this.position = position; + this.number = number; + this.al = al; + + alIndex = new int[seqs.length]; + for (int i = 0; i < seqs.length; i++) + { + alIndex[i] = al.findIndex(seqs[i]); + } + + fullAlignmentHeight = (al.getHeight() == seqs.length); + } + } + +} diff --git a/src/jalview/commands/OrderCommand.java b/src/jalview/commands/OrderCommand.java new file mode 100644 index 0000000..57097c0 --- /dev/null +++ b/src/jalview/commands/OrderCommand.java @@ -0,0 +1,62 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +package jalview.commands; + +import jalview.analysis.*; +import jalview.datamodel.*; + +public class OrderCommand + implements CommandI +{ + String description; + SequenceI[] seqs; + SequenceI[] seqs2; + AlignmentI al; + + public OrderCommand(String description, + SequenceI[] seqs, + AlignmentI al) + { + this.description = description; + this.seqs = seqs; + this.seqs2 = al.getSequencesArray(); + this.al = al; + doCommand(); + } + + public String getDescription() + { + return description; + } + + public int getSize() + { + return 1; + } + + public void doCommand() + { + AlignmentSorter.setOrder(al, seqs2); + } + + public void undoCommand() + { + AlignmentSorter.setOrder(al, seqs); + } +} diff --git a/src/jalview/commands/RemoveGapColCommand.java b/src/jalview/commands/RemoveGapColCommand.java new file mode 100644 index 0000000..1e9b832 --- /dev/null +++ b/src/jalview/commands/RemoveGapColCommand.java @@ -0,0 +1,106 @@ +package jalview.commands; + +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +import jalview.datamodel.*; + +public class RemoveGapColCommand + extends EditCommand +{ + int columnsDeleted; + public RemoveGapColCommand(String description, + SequenceI[] seqs, + int start, int end, AlignmentI al) + { + this.description = description; + + int j, jSize = seqs.length; + + int startCol = -1, endCol = -1; + columnsDeleted = 0; + + edits = new Edit[0]; + + boolean delete = true; + for (int i = start; i <= end; i++) + { + delete = true; + + for (j = 0; j < jSize; j++) + { + if (seqs[j].getLength() > i) + { + if (!jalview.util.Comparison.isGap(seqs[j].getCharAt(i))) + { + if (delete) + { + endCol = i; + } + + delete = false; + break; + } + } + } + + if (delete && startCol == -1) + { + startCol = i; + } + + if (!delete && startCol > -1) + { + this.appendEdit(DELETE_GAP, seqs, + startCol - columnsDeleted, + endCol - startCol, + al, + false); + + columnsDeleted += (endCol - startCol); + startCol = -1; + endCol = -1; + } + } + + if (delete && startCol > -1) + { + //This is for empty columns at the + //end of the alignment + + this.appendEdit(DELETE_GAP, seqs, + startCol - columnsDeleted, + end - startCol + 1, + al, + false); + + columnsDeleted += (end - startCol + 1); + } + + performEdit(0); + } + + public int getSize() + { + //We're interested in the number of columns deleted, + //Not the number of sequence edits. + return columnsDeleted; + } + +} diff --git a/src/jalview/commands/RemoveGapsCommand.java b/src/jalview/commands/RemoveGapsCommand.java new file mode 100644 index 0000000..ccc8008 --- /dev/null +++ b/src/jalview/commands/RemoveGapsCommand.java @@ -0,0 +1,120 @@ +package jalview.commands; + +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +import jalview.datamodel.*; + +public class RemoveGapsCommand + extends EditCommand +{ + public RemoveGapsCommand(String description, + SequenceI[] seqs, AlignmentI al) + { + this.description = description; + int width = 0; + for (int i = 0; i < seqs.length; i++) + { + if (seqs[i].getLength() > width) + { + width = seqs[i].getLength(); + } + } + + findGaps(seqs, 0, width, al); + } + + public RemoveGapsCommand(String description, + SequenceI[] seqs, + int start, int end, AlignmentI al) + { + this.description = description; + findGaps(seqs, start, end, al); + } + + void findGaps(SequenceI[] seqs, int start, int end, AlignmentI al) + { + + int startCol = -1, endCol = -1; + int deletedCols = 0; + + int j, jSize; + + edits = new Edit[0]; + + boolean delete = true; + char[] sequence; + + for (int s = 0; s < seqs.length; s++) + { + deletedCols = 0; + startCol = -1; + endCol = -1; + sequence = seqs[s].getSequence(start, end + 1); + + jSize = sequence.length; + for (j = 0; j < jSize; j++) + { + delete = true; + + if (!jalview.util.Comparison.isGap(sequence[j])) + { + if (delete) + { + endCol = j; + } + + delete = false; + } + + if (delete && startCol == -1) + { + startCol = j; + } + + if (!delete && startCol > -1) + { + this.appendEdit(DELETE_GAP, new SequenceI[] + {seqs[s]}, + start + startCol - deletedCols, + endCol - startCol, + al, + false); + + deletedCols += (endCol - startCol); + startCol = -1; + endCol = -1; + } + } + if (delete && startCol > -1) + { + this.appendEdit(DELETE_GAP, new SequenceI[] + {seqs[s]}, + start + startCol - deletedCols, + jSize - startCol, + al, + false); + } + + } + + performEdit(0); + } + +} diff --git a/src/jalview/commands/TrimRegionCommand.java b/src/jalview/commands/TrimRegionCommand.java new file mode 100644 index 0000000..4c1dea0 --- /dev/null +++ b/src/jalview/commands/TrimRegionCommand.java @@ -0,0 +1,178 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +package jalview.commands; + +import java.util.*; + +import jalview.datamodel.*; +import jalview.util.*; + +public class TrimRegionCommand + extends EditCommand +{ + public static String TRIM_LEFT = "TrimLeft"; + public static String TRIM_RIGHT = "TrimRight"; + + public ColumnSelection colSel = null; + + int[] start; + + ShiftList shiftList; + + SequenceGroup selectionGroup; + + Vector deletedHiddenColumns; + + int columnsDeleted; + + public TrimRegionCommand(String description, + String command, + SequenceI[] seqs, + int column, + AlignmentI al, + ColumnSelection colSel, + SequenceGroup selectedRegion) + { + this.description = description; + this.selectionGroup = selectedRegion; + this.colSel = colSel; + if (command.equalsIgnoreCase(TRIM_LEFT)) + { + if (column == 0) + { + return; + } + + columnsDeleted = column; + + edits = new Edit[] + { + new Edit(CUT, seqs, 0, column, al)}; + } + else if (command.equalsIgnoreCase(TRIM_RIGHT)) + { + int width = al.getWidth() - column - 1; + if (width < 2) + { + return; + } + + columnsDeleted = width - 1; + + edits = new Edit[] + { + new Edit(CUT, seqs, column + 1, width, al)}; + } + + //We need to keep a record of the sequence start + //in order to restore the state after a redo + int i, isize = edits[0].seqs.length; + start = new int[isize]; + for (i = 0; i < isize; i++) + { + start[i] = edits[0].seqs[i].getStart(); + } + + performEdit(0); + } + + void cut(Edit command) + { + int column, j, jSize = command.seqs.length; + for (j = 0; j < jSize; j++) + { + if (command.position == 0) + { + //This is a TRIM_LEFT command + column = command.seqs[j].findPosition(command.number); + command.seqs[j].setStart(column); + } + else + { + //This is a TRIM_RIGHT command + column = command.seqs[j].findPosition(command.position) - 1; + command.seqs[j].setEnd(column); + } + } + + super.cut(command); + + if (command.position == 0) + { + deletedHiddenColumns = colSel.compensateForEdit(0, command.number); + if (selectionGroup != null) + { + selectionGroup.adjustForRemoveLeft(command.number); + } + } + else + { + deletedHiddenColumns = colSel.compensateForEdit(command.position, + command.number); + if (selectionGroup != null) + { + selectionGroup.adjustForRemoveRight(command.position); + } + } + } + + void paste(Edit command) + { + super.paste(command); + int column, j, jSize = command.seqs.length; + for (j = 0; j < jSize; j++) + { + if (command.position == 0) + { + command.seqs[j].setStart(start[j]); + } + else + { + column = command.seqs[j] + .findPosition(command.number + command.position) - 1; + command.seqs[j].setEnd(column); + } + } + + if (command.position == 0) + { + colSel.compensateForEdit(0, -command.number); + if (selectionGroup != null) + { + selectionGroup.adjustForRemoveLeft( -command.number); + } + } + + if (deletedHiddenColumns != null) + { + int[] region; + for (int i = 0; i < deletedHiddenColumns.size(); i++) + { + region = (int[]) deletedHiddenColumns.elementAt(i); + colSel.hideColumns(region[0], region[1]); + } + } + } + + public int getSize() + { + return columnsDeleted; + } + +} diff --git a/src/jalview/gui/EditNameDialog.java b/src/jalview/gui/EditNameDialog.java new file mode 100644 index 0000000..b6cc5a5 --- /dev/null +++ b/src/jalview/gui/EditNameDialog.java @@ -0,0 +1,80 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package jalview.gui; + +import java.awt.*; +import javax.swing.*; + +public class EditNameDialog +{ + JTextField id, description; + JButton ok = new JButton("Accept"); + JButton cancel = new JButton("Cancel"); + boolean accept = false; + + public String getName() + { + return id.getText(); + } + + public String getDescription() + { + if (description.getText().length() < 1) + { + return null; + } + else + { + return description.getText(); + } + } + + public EditNameDialog(String name, + String desc, + String label1, + String label2, + String title) + { + JLabel idlabel = new JLabel(label1); + JLabel desclabel = new JLabel(label2); + idlabel.setFont(new Font("Courier", Font.PLAIN, 12)); + desclabel.setFont(new Font("Courier", Font.PLAIN, 12)); + id = new JTextField(name, 40); + description = new JTextField(desc, 40); + JPanel panel = new JPanel(new BorderLayout()); + JPanel panel2 = new JPanel(new BorderLayout()); + panel2.add(idlabel, BorderLayout.WEST); + panel2.add(id, BorderLayout.CENTER); + panel.add(panel2, BorderLayout.NORTH); + panel2 = new JPanel(new BorderLayout()); + panel2.add(desclabel, BorderLayout.WEST); + panel2.add(description, BorderLayout.CENTER); + panel.add(panel2, BorderLayout.SOUTH); + + int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop, + panel, title, + JOptionPane.OK_CANCEL_OPTION); + + if (reply == JOptionPane.OK_OPTION) + { + accept = true; + } + } +} diff --git a/src/jalview/gui/TextColourChooser.java b/src/jalview/gui/TextColourChooser.java new file mode 100644 index 0000000..504e1d5 --- /dev/null +++ b/src/jalview/gui/TextColourChooser.java @@ -0,0 +1,213 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package jalview.gui; + +import java.util.*; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; + +import jalview.datamodel.*; + +public class TextColourChooser +{ + AlignmentPanel ap; + SequenceGroup sg; + + public void chooseColour(AlignmentPanel ap, SequenceGroup sg) + { + this.ap = ap; + this.sg = sg; + + int original1, original2, originalThreshold; + if (sg == null) + { + original1 = ap.av.textColour.getRGB(); + original2 = ap.av.textColour2.getRGB(); + originalThreshold = ap.av.thresholdTextColour; + } + else + { + original1 = sg.textColour.getRGB(); + original2 = sg.textColour2.getRGB(); + originalThreshold = sg.thresholdTextColour; + } + + final JSlider slider = new JSlider(0, 750, originalThreshold); + final JPanel col1 = new JPanel(); + col1.setPreferredSize(new Dimension(40, 20)); + col1.setBorder(BorderFactory.createEtchedBorder()); + col1.setToolTipText("Dark Colour"); + col1.setBackground(new Color(original1)); + final JPanel col2 = new JPanel(); + col2.setPreferredSize(new Dimension(40, 20)); + col2.setBorder(BorderFactory.createEtchedBorder()); + col2.setToolTipText("Light Colour"); + col2.setBackground(new Color(original2)); + final JPanel bigpanel = new JPanel(new BorderLayout()); + JPanel panel = new JPanel(); + bigpanel.add(panel, BorderLayout.CENTER); + bigpanel.add(new JLabel( + "Select a dark and light text colour, then set the threshold to" + + "
switch between colours, based on background colour
"), + BorderLayout.NORTH); + panel.add(col1); + panel.add(slider); + panel.add(col2); + + col1.addMouseListener(new MouseAdapter() + { + public void mousePressed(MouseEvent e) + { + Color col = JColorChooser.showDialog(bigpanel, + "Select Colour for Text", + col1.getBackground()); + if (col != null) + { + colour1Changed(col); + col1.setBackground(col); + } + } + }); + + col2.addMouseListener(new MouseAdapter() + { + public void mousePressed(MouseEvent e) + { + Color col = JColorChooser.showDialog(bigpanel, + "Select Colour for Text", + col2.getBackground()); + if (col != null) + { + colour2Changed(col); + col2.setBackground(col); + } + } + }); + + slider.addChangeListener(new ChangeListener() + { + public void stateChanged(ChangeEvent evt) + { + thresholdChanged(slider.getValue()); + } + }); + + int reply = JOptionPane.showInternalOptionDialog( + ap, + bigpanel, + "Adjust Foreground Text Colour Threshold", + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + null, null); + + if (reply == JOptionPane.CANCEL_OPTION) + { + if (sg == null) + { + ap.av.textColour = new Color(original1); + ap.av.textColour2 = new Color(original2); + ap.av.thresholdTextColour = originalThreshold; + } + else + { + sg.textColour = new Color(original1); + sg.textColour2 = new Color(original2); + sg.thresholdTextColour = originalThreshold; + } + } + } + + void colour1Changed(Color col) + { + if (sg == null) + { + ap.av.textColour = col; + if (ap.av.colourAppliesToAllGroups) + { + setGroupTextColour(); + } + } + else + { + sg.textColour = col; + } + + ap.repaint(); + } + + void colour2Changed(Color col) + { + if (sg == null) + { + ap.av.textColour2 = col; + if (ap.av.colourAppliesToAllGroups) + { + setGroupTextColour(); + } + } + else + { + sg.textColour2 = col; + } + + ap.repaint(); + } + + void thresholdChanged(int value) + { + if (sg == null) + { + ap.av.thresholdTextColour = value; + if (ap.av.colourAppliesToAllGroups) + { + setGroupTextColour(); + } + } + else + { + sg.thresholdTextColour = value; + } + + ap.repaint(); + } + + void setGroupTextColour() + { + if (ap.av.alignment.getGroups() == null) + { + return; + } + + Vector groups = ap.av.alignment.getGroups(); + + for (int i = 0; i < groups.size(); i++) + { + SequenceGroup sg = (SequenceGroup) groups.elementAt(i); + sg.textColour = ap.av.textColour; + sg.textColour2 = ap.av.textColour2; + sg.thresholdTextColour = ap.av.thresholdTextColour; + } + } + +} diff --git a/src/jalview/gui/UserQuestionnaireCheck.java b/src/jalview/gui/UserQuestionnaireCheck.java new file mode 100644 index 0000000..0accc9c --- /dev/null +++ b/src/jalview/gui/UserQuestionnaireCheck.java @@ -0,0 +1,140 @@ +package jalview.gui; + +import java.io.*; +import java.net.*; + +import javax.swing.*; + +public class UserQuestionnaireCheck + implements Runnable +{ + /** + * Implements the client side machinery for detecting a new questionnaire, + * checking if the user has responded to an existing one, + * and prompting the user for responding to a questionnaire. + * This is intended to work with the perl CGI scripts checkresponder.pl and + * questionnaire.pl + */ + String url = null; + UserQuestionnaireCheck(String url) + { + if (url.indexOf("questionnaire.pl") == -1) + { + jalview.bin.Cache.log.error("'" + url + + "' is an Invalid URL for the checkForQuestionnaire() method.\n" + + "This argument is only for questionnaires derived from jalview's questionnaire.pl cgi interface."); + } + else + { + this.url = url; + } + } + + String qid = null, rid = null; + private boolean checkresponse(URL qurl) + throws Exception + { + jalview.bin.Cache.log.debug("Checking Response for : " + qurl); + boolean prompt = false; + // see if we have already responsed to this questionnaire or get a new qid/rid pair + BufferedReader br = new BufferedReader(new InputStreamReader(qurl. + openStream())); + String qresp; + while ( (qresp = br.readLine()) != null) + { + if (qresp.indexOf("NOTYET:") == 0) + { + prompt = true; // not yet responded under that ID + } + else + { + if (qresp.indexOf("QUESTIONNAIRE:") == 0) + { + // QUESTIONNAIRE:qid:rid for the latest questionnaire. + int p = qresp.indexOf(':', 14); + if (p > -1) + { + rid = null; + qid = qresp.substring(14, p); + if (p < (qresp.length() - 1)) + { + rid = qresp.substring(p + 1); + prompt = true; // this is a new qid/rid pair + } + } + } + } + } + return prompt; + } + + public void run() + { + if (url == null) + { + return; + } + boolean prompt = false; + try + { + // First - check to see if wee have an old questionnaire/response id pair. + String lastq = jalview.bin.Cache.getProperty("QUESTIONNAIRE"); + if (lastq == null) + { + prompt = checkresponse(new URL(url + (url.indexOf('?') > -1 ? "&" : "?") + + "checkresponse=1")); + } + else + { + String qurl = url + (url.indexOf('?') > -1 ? "&" : "?") + + "checkresponse=1"; + // query the server with the old qid/id pair + String qqid = lastq.indexOf(':') > -1 ? + lastq.substring(0, lastq.indexOf(':')) : null; + if (qqid != null && qqid != "null" && qqid.length() > 0) + { + qurl += "&qid=" + qqid; + qid = qqid; + String qrid = lastq.substring(lastq.indexOf(':') + 1); // retrieve old rid + if (qrid != null && !qrid.equals("null")) + { + rid = qrid; + qurl += "&rid=" + qrid; + } + } + // see if we have already responsed to this questionnaire. + prompt = checkresponse(new URL(qurl)); + } + if (qid != null && rid != null) + { + // Update our local property cache with latest qid and rid + jalview.bin.Cache.setProperty("QUESTIONNAIRE", qid + ":" + rid); + } + if (prompt) + { + String qurl = url + (url.indexOf('?') > -1 ? "&" : "?") + "qid=" + qid + + "&rid=" + rid; + jalview.bin.Cache.log.info("Prompting user for questionnaire at " + + qurl); + int reply = JOptionPane.showInternalConfirmDialog(Desktop.desktop, + "There is a new Questionnaire available." + + "Would you like to complete it now ?\n", + "Jalview User Survey", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE); + + if (reply == JOptionPane.YES_OPTION) + { + jalview.bin.Cache.log.debug("Opening " + qurl); + jalview.util.BrowserLauncher.openURL(qurl); + } + } + } + catch (Exception e) + { + jalview.bin.Cache.log.warn("When trying to access questionnaire URL " + + url, e); + } + } + +} diff --git a/src/jalview/gui/VamsasClient.java b/src/jalview/gui/VamsasClient.java new file mode 100755 index 0000000..7c37bd3 --- /dev/null +++ b/src/jalview/gui/VamsasClient.java @@ -0,0 +1,275 @@ +/** + * + */ +package jalview.gui; + +import java.io.File; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Hashtable; +import java.util.IdentityHashMap; +import java.util.Vector; +import java.util.jar.JarOutputStream; + +import javax.swing.JInternalFrame; + +import jalview.bin.Cache; +import jalview.io.VamsasDatastore; + +import org.vamsas.client.UserHandle; +import org.vamsas.client.simpleclient.FileWatcher; +import org.vamsas.client.simpleclient.VamsasArchive; +import org.vamsas.client.simpleclient.VamsasFile; +import org.vamsas.objects.core.Entry; +import org.vamsas.objects.core.VamsasDocument; +import org.vamsas.test.simpleclient.ArchiveClient; +import org.vamsas.test.simpleclient.ClientDoc; + +/** + * @author jimp + * + */ +public class VamsasClient + extends ArchiveClient +{ + // Cache.preferences for vamsas client session arena + // preferences for check for default session at startup. + // user and organisation stuff. + public VamsasClient(Desktop jdesktop, + File sessionPath) + { + super(System.getProperty("user.name"), System.getProperty("host.name"), + "jalview", "2.7", + sessionPath); + } + + public void initial_update() + { + Cache.log.info("Jalview loading the Vamsas Session."); + // load in the vamsas archive for the first time + ClientDoc cdoc = this.getUpdateable(); + updateJalview(cdoc); + JInternalFrame[] frames = Desktop.desktop.getAllFrames(); + + if (frames == null) + { + return; + } + + try + { + //REVERSE ORDER + for (int i = frames.length - 1; i > -1; i--) + { + if (frames[i] instanceof AlignFrame) + { + AlignFrame af = (AlignFrame) frames[i]; + af.alignPanel.alignmentChanged(); + } + } + } + catch (Exception e) + { + Cache.log.warn( + "Exception whilst refreshing jalview windows after a vamsas document update.", + e); + } + doUpdate(cdoc); + cdoc.closeDoc(); + } + + /** + * this will close all windows currently in Jalview. + * + + protected void closeWindows() { + JInternalFrame[] frames = Desktop.desktop.getAllFrames(); + + if (frames == null) + { + return; + } + + try + { + for (int i = frames.length - 1; i > -1; i--) { + frames[i].dispose(); + } + } catch (Exception e) { + Cache.log.error("Whilst closing windows",e); + } + + } + + public void get_update(VamsasArchive doc) { + // Close windows - load update. + Cache.log.info("Jalview updating from Vamsas Session."); + } + */ + VamsasClientWatcher watcher = null; + public void push_update() + { + watchForChange = false; + try + { + Thread.sleep(WATCH_SLEEP); + } + catch (Exception e) + { + + } + ; + ClientDoc cdoc = getUpdateable(); + updateVamsasDocument(cdoc); + doUpdate(cdoc); + cdoc.closeDoc(); + cdoc = null; + watchForChange = true; + if (watcher != null) + { + watcher.start(); + } + // collect all uncached alignments and put them into the vamsas dataset. + // store them. + Cache.log.info("Jalview updating the Vamsas Session."); + } + + public void end_session() + { + // stop any update/watcher thread. + watchForChange = false; // this makes any watch(long) loops return. + // we should also wait arount for this.WATCH_SLEEP to really make sure the watcher thread has stopped. + try + { + Thread.sleep(WATCH_SLEEP); + } + catch (Exception e) + { + + } + ; + Cache.log.info("Jalview disconnecting from the Vamsas Session."); + } + + public void updateJalview(ClientDoc cdoc) + { + ensureJvVamsas(); + VamsasDatastore vds = new VamsasDatastore(cdoc, vobj2jv, jv2vobj, + baseProvEntry()); + vds.updateToJalview(); + } + + private void ensureJvVamsas() + { + if (jv2vobj == null) + { + jv2vobj = new IdentityHashMap(); + vobj2jv = new Hashtable(); + } + } + + /** + * jalview object binding to VorbaIds + */ + IdentityHashMap jv2vobj = null; + Hashtable vobj2jv = null; + public void updateVamsasDocument(ClientDoc doc) + { + ensureJvVamsas(); + VamsasDatastore vds = new VamsasDatastore(doc, vobj2jv, jv2vobj, + baseProvEntry()); + // wander through frames + JInternalFrame[] frames = Desktop.desktop.getAllFrames(); + + if (frames == null) + { + return; + } + + try + { + //REVERSE ORDER + for (int i = frames.length - 1; i > -1; i--) + { + if (frames[i] instanceof AlignFrame) + { + AlignFrame af = (AlignFrame) frames[i]; + + // update alignment and root from frame. + vds.storeVAMSAS(af.getViewport(), af.getTitle()); + } + } + } + catch (Exception e) + { + Cache.log.error("Vamsas Document store exception", e); + } + } + + private Entry baseProvEntry() + { + org.vamsas.objects.core.Entry pentry = new org.vamsas.objects.core.Entry(); + pentry.setUser(this.getProvenanceUser()); + pentry.setApp(this.getClientHandle().getClientName()); + pentry.setDate(new org.exolab.castor.types.Date(new java.util.Date())); + pentry.setAction("created"); + return pentry; + } + + protected class VamsasClientWatcher + extends Thread + { + /* (non-Javadoc) + * @see java.lang.Thread#run() + */ + VamsasClient client = null; + VamsasClientWatcher(VamsasClient client) + { + this.client = client; + } + + boolean running = false; + public void run() + { + running = true; + while (client.watchForChange) + { + ClientDoc docio = client.watch(0); + if (docio != null) + { + client.disableGui(true); + Cache.log.debug("Updating jalview from changed vamsas document."); + client.updateJalview(docio); + Cache.log.debug("Finished updating from document change."); + docio.closeDoc(); + docio = null; + client.disableGui(false); + } + } + running = false; + + } + + } + + public void disableGui(boolean b) + { + Desktop.instance.setVamsasUpdate(b); + } + + public void startWatcher() + { + if (watcher == null) + { + watcher = new VamsasClientWatcher(this); + } + Thread thr = new Thread() + { + public void run() + { + watcher.start(); + } + }; + thr.start(); + } +} diff --git a/src/jalview/io/AMSAFile.java b/src/jalview/io/AMSAFile.java new file mode 100644 index 0000000..1f3fca0 --- /dev/null +++ b/src/jalview/io/AMSAFile.java @@ -0,0 +1,96 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +package jalview.io; + +import jalview.datamodel.*; + +public class AMSAFile + extends jalview.io.FastaFile +{ + + AlignmentI al; + /** + * Creates a new AMSAFile object for output. + */ + public AMSAFile(AlignmentI al) + { + this.al = al; + } + + /** + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public String print() + { + super.print(getSeqsAsArray()); + + AlignmentAnnotation aa; + if (al.getAlignmentAnnotation() != null) + { + for (int i = 0; i < al.getAlignmentAnnotation().length; i++) + { + aa = al.getAlignmentAnnotation()[i]; + if (aa.autoCalculated || !aa.visible) + { + continue; + } + + out.append(">#_" + aa.label); + if (aa.description != null) + { + out.append(" " + aa.description); + } + + out.append("\n"); + + int nochunks = (aa.annotations.length / len) + 1; + + for (int j = 0; j < nochunks; j++) + { + int start = j * len; + int end = start + len; + if (end > aa.annotations.length) + { + end = aa.annotations.length; + } + + String ch; + for (int k = start; k < end; k++) + { + if (aa.annotations[k] == null) + { + ch = " "; + } + else + { + ch = aa.annotations[k].displayCharacter; + } + + out.append(ch); + + } + out.append("\n"); + } + } + } + return out.toString(); + } +} diff --git a/src/jalview/io/VamsasDatastore.java b/src/jalview/io/VamsasDatastore.java new file mode 100755 index 0000000..f1cba4f --- /dev/null +++ b/src/jalview/io/VamsasDatastore.java @@ -0,0 +1,1900 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +package jalview.io; + +import jalview.bin.Cache; +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentView; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceI; +import jalview.gui.AlignFrame; +import jalview.gui.AlignViewport; +import jalview.gui.Desktop; +import jalview.gui.TreePanel; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.IdentityHashMap; +import java.util.Vector; + +import org.vamsas.client.Vobject; +import org.vamsas.client.VorbaId; +import org.vamsas.objects.core.Alignment; +import org.vamsas.objects.core.AlignmentSequence; +import org.vamsas.objects.core.AlignmentSequenceAnnotation; +import org.vamsas.objects.core.AnnotationElement; +import org.vamsas.objects.core.DataSet; +import org.vamsas.objects.core.DataSetAnnotations; +import org.vamsas.objects.core.DbRef; +import org.vamsas.objects.core.Entry; +import org.vamsas.objects.core.Glyph; +import org.vamsas.objects.core.Input; +import org.vamsas.objects.core.Link; +import org.vamsas.objects.core.Newick; +import org.vamsas.objects.core.Param; +import org.vamsas.objects.core.Property; +import org.vamsas.objects.core.Provenance; +import org.vamsas.objects.core.RangeAnnotation; +import org.vamsas.objects.core.RangeType; +import org.vamsas.objects.core.Seg; +import org.vamsas.objects.core.Sequence; +import org.vamsas.objects.core.Tree; +import org.vamsas.objects.core.VAMSAS; +import org.vamsas.test.simpleclient.ClientDoc; + +/* + * + * static { + * org.exolab.castor.util.LocalConfiguration.getInstance().getProperties().setProperty( + * "org.exolab.castor.serializer", "org.apache.xml.serialize.XMLSerilazizer"); } + * + */ + +public class VamsasDatastore +{ + Entry provEntry = null; + + // AlignViewport av; + + org.exolab.castor.types.Date date = new org.exolab.castor.types.Date( + new java.util.Date()); + + ClientDoc cdoc; + + Hashtable vobj2jv; + + IdentityHashMap jv2vobj; + + public VamsasDatastore(ClientDoc cdoc, Hashtable vobj2jv, + IdentityHashMap jv2vobj, Entry provEntry) + { + this.cdoc = cdoc; + this.vobj2jv = vobj2jv; + this.jv2vobj = jv2vobj; + this.provEntry = provEntry; + } + + /* + * public void storeJalview(String file, AlignFrame af) { try { // 1. Load the + * mapping information from the file Mapping map = new + * Mapping(getClass().getClassLoader()); java.net.URL url = + * getClass().getResource("/jalview_mapping.xml"); map.loadMapping(url); // 2. + * Unmarshal the data // Unmarshaller unmar = new Unmarshaller(); + * //unmar.setIgnoreExtraElements(true); //unmar.setMapping(map); // uni = + * (UniprotFile) unmar.unmarshal(new FileReader(file)); // 3. marshal the data + * with the total price back and print the XML in the console Marshaller + * marshaller = new Marshaller( new FileWriter(file) ); + * + * marshaller.setMapping(map); marshaller.marshal(af); } catch (Exception e) { + * e.printStackTrace(); } } + * + * + */ + /** + * @return the Vobject bound to Jalview datamodel object + */ + protected Vobject getjv2vObj(Object jvobj) + { + if (jv2vobj.containsKey(jvobj)) + { + return cdoc.getObject( (VorbaId) jv2vobj.get(jvobj)); + } + return null; + } + + /** + * + * @param vobj + * @return Jalview datamodel object bound to the vamsas document object + */ + protected Object getvObj2jv(org.vamsas.client.Vobject vobj) + { + VorbaId id = vobj.getVorbaId(); + if (id == null) + { + id = cdoc.registerObject(vobj); + Cache.log + .debug("Registering new object and returning null for getvObj2jv"); + return null; + } + if (vobj2jv.containsKey(vobj.getVorbaId())) + { + return vobj2jv.get(vobj.getVorbaId()); + } + return null; + } + + protected void bindjvvobj(Object jvobj, org.vamsas.client.Vobject vobj) + { + VorbaId id = vobj.getVorbaId(); + if (id == null) + { + id = cdoc.registerObject(vobj); + if (id == null || vobj.getVorbaId() == null) + { + Cache.log.error("Failed to get id for " + + (vobj.isRegisterable() ? "registerable" : + "unregisterable") + " object " + vobj); + } + } + + if (vobj2jv.containsKey(vobj.getVorbaId()) && + ! ( (VorbaId) vobj2jv.get(vobj.getVorbaId())).equals(jvobj)) + { + Cache.log.debug("Warning? Overwriting existing vamsas id binding for " + + vobj.getVorbaId(), + new Exception("Overwriting vamsas id binding.")); + } + else if (jv2vobj.containsKey(jvobj) && + ! ( (VorbaId) jv2vobj.get(jvobj)).equals(vobj.getVorbaId())) + { + Cache.log.debug( + "Warning? Overwriting existing jalview object binding for " + jvobj, + new Exception("Overwriting jalview object binding.")); + } + /* Cache.log.error("Attempt to make conflicting object binding! "+vobj+" id " +vobj.getVorbaId()+" already bound to "+getvObj2jv(vobj)+" and "+jvobj+" already bound to "+getjv2vObj(jvobj),new Exception("Excessive call to bindjvvobj")); + }*/ + // we just update the hash's regardless! + vobj2jv.put(vobj.getVorbaId(), jvobj); + // JBPNote - better implementing a hybrid invertible hash. + jv2vobj.put(jvobj, vobj.getVorbaId()); + } + + /** + * put the alignment viewed by AlignViewport into cdoc. + * + * @param av alignViewport to be stored + * @param aFtitle title for alignment + */ + public void storeVAMSAS(AlignViewport av, String aFtitle) + { + try + { + jalview.datamodel.AlignmentI jal = av.getAlignment(); + boolean nw = false; + VAMSAS root = null; // will be resolved based on Dataset Parent. + // ///////////////////////////////////////// + // SAVE THE DATASET + if (jal.getDataset() == null) + { + Cache.log.warn("Creating new dataset for an alignment."); + jal.setDataset(null); + } + DataSet dataset = (DataSet) getjv2vObj(jal.getDataset()); + if (dataset == null) + { + root = cdoc.getVamsasRoots()[0]; // default vamsas root for modifying. + dataset = new DataSet(); + root.addDataSet(dataset); + bindjvvobj(jal.getDataset(), dataset); + dataset.setProvenance(dummyProvenance()); + dataset.getProvenance().addEntry(provEntry); + nw = true; + } + else + { + root = (VAMSAS) dataset.getV_parent(); + } + // update dataset + Sequence sequence; + DbRef dbref; + // set new dataset and alignment sequences based on alignment Nucleotide + // flag. + // this *will* break when alignment contains both nucleotide and amino + // acid sequences. + String dict = jal.isNucleotide() ? + org.vamsas.objects.utils.SymbolDictionary.STANDARD_NA + : org.vamsas.objects.utils.SymbolDictionary.STANDARD_AA; + for (int i = 0; i < jal.getHeight(); i++) + { + SequenceI sq = jal.getSequenceAt(i).getDatasetSequence(); // only insert + // referenced + // sequences + // to dataset. + sequence = (Sequence) getjv2vObj(sq); + if (sequence == null) + { + sequence = new Sequence(); + bindjvvobj(sq, sequence); + sq.setVamsasId(sequence.getVorbaId().getId()); + sequence.setSequence(sq.getSequenceAsString()); + sequence.setDictionary(dict); + sequence.setName(jal.getDataset().getSequenceAt(i).getName()); + sequence.setStart(jal.getDataset().getSequenceAt(i).getStart()); + sequence.setEnd(jal.getDataset().getSequenceAt(i).getEnd()); + dataset.addSequence(sequence); + } + else + { + // verify principal attributes. and update any new + // features/references. + System.out.println("update dataset sequence object."); + } + if (sq.getSequenceFeatures() != null) + { + int sfSize = sq.getSequenceFeatures().length; + + for (int sf = 0; sf < sfSize; sf++) + { + jalview.datamodel.SequenceFeature feature = (jalview.datamodel. + SequenceFeature) sq + .getSequenceFeatures()[sf]; + + DataSetAnnotations dsa = (DataSetAnnotations) getjv2vObj(feature); + if (dsa == null) + { + dsa = (DataSetAnnotations) getDSAnnotationFromJalview( + new DataSetAnnotations(), feature); + if (dsa.getProvenance() == null) + { + dsa.setProvenance(new Provenance()); + } + addProvenance(dsa.getProvenance(), "created"); // JBPNote - need + // to update + dsa.setSeqRef(sequence); + bindjvvobj(feature, dsa); + dataset.addDataSetAnnotations(dsa); + } + else + { + // todo: verify and update dataset annotations for sequence + System.out.println("update dataset sequence annotations."); + } + } + } + + if (sq.getDBRef() != null) + { + DBRefEntry[] entries = sq.getDBRef(); + jalview.datamodel.DBRefEntry dbentry; + for (int db = 0; db < entries.length; db++) + { + dbentry = entries[db]; + dbref = (DbRef) getjv2vObj(dbentry); + if (dbref == null) + { + dbref = new DbRef(); + bindjvvobj(dbentry, dbref); + dbref.setAccessionId(dbentry.getAccessionId()); + dbref.setSource(dbentry.getSource()); + dbref.setVersion(dbentry.getVersion()); + /* + * TODO: Maps are not yet supported by Jalview. Map vMap = new + * Map(); vMap.set dbref.addMap(vMap); + */ + sequence.addDbRef(dbref); + } + else + { + // TODO: verify and update dbrefs in vamsas document + // there will be trouble when a dataset sequence is modified to + // contain more residues than were originally referenced - we must + // then make a number of dataset sequence entries + System.out + .println("update dataset sequence database references."); + } + } + + } + } + // dataset.setProvenance(getVamsasProvenance(jal.getDataset().getProvenance())); + // //////////////////////////////////////////// + + // //////////////////////////////////////////// + // Save the Alignments + + Alignment alignment = (Alignment) getjv2vObj(av); // this is so we can get the alignviewport back + if (alignment == null) + { + alignment = new Alignment(); + bindjvvobj(av, alignment); + if (alignment.getProvenance() == null) + { + alignment.setProvenance(new Provenance()); + } + addProvenance(alignment.getProvenance(), "added"); // TODO: insert some + // sensible source + // here + dataset.addAlignment(alignment); + { + Property title = new Property(); + title.setName("jalview:AlTitle"); + title.setType("string"); + title.setContent(aFtitle); + alignment.addProperty(title); + } + alignment.setGapChar(String.valueOf(av.getGapCharacter())); + AlignmentSequence alseq = null; + for (int i = 0; i < jal.getHeight(); i++) + { + alseq = new AlignmentSequence(); + // TODO: VAMSAS: translate lowercase symbols to annotation ? + alseq.setSequence(jal.getSequenceAt(i).getSequenceAsString()); + alseq.setName(jal.getSequenceAt(i).getName()); + alseq.setStart(jal.getSequenceAt(i).getStart()); + alseq.setEnd(jal.getSequenceAt(i).getEnd()); + alseq.setRefid(getjv2vObj(jal.getSequenceAt(i).getDatasetSequence())); + alignment.addAlignmentSequence(alseq); + bindjvvobj(jal.getSequenceAt(i), alseq); + } + } + else + { + // todo: verify and update mutable alignment props. + if (alignment.getModifiable()) + { + System.out.println("update alignment in document."); + } + else + { + System.out + .println("update edited alignment to new alignment in document."); + } + } + // //////////////////////////////////////////// + // SAVE Alignment Sequence Features + for (int i = 0, iSize = alignment.getAlignmentSequenceCount(); i < iSize; + i++) + { + AlignmentSequence valseq; + SequenceI alseq = (SequenceI) getvObj2jv(valseq = alignment + .getAlignmentSequence(i)); + if (alseq != null && alseq.getSequenceFeatures() != null) + { + jalview.datamodel.SequenceFeature[] features = alseq + .getSequenceFeatures(); + for (int f = 0; f < features.length; f++) + { + if (features[f] != null) + { + AlignmentSequenceAnnotation valseqf = ( + AlignmentSequenceAnnotation) getjv2vObj(features[i]); + if (valseqf == null) + { + + valseqf = (AlignmentSequenceAnnotation) + getDSAnnotationFromJalview( + new AlignmentSequenceAnnotation(), features[i]); + if (valseqf.getProvenance() == null) + { + valseqf.setProvenance(new Provenance()); + } + addProvenance(valseqf.getProvenance(), "created"); // JBPNote - + // need to + // update + bindjvvobj(features[i], valseqf); + valseq.addAlignmentSequenceAnnotation(valseqf); + } + } + + } + } + } + + // //////////////////////////////////////////// + // SAVE ANNOTATIONS + if (jal.getAlignmentAnnotation() != null) + { + jalview.datamodel.AlignmentAnnotation[] aa = jal + .getAlignmentAnnotation(); + java.util.HashMap AlSeqMaps = new HashMap(); // stores int maps from + // alignment columns to + // sequence positions. + for (int i = 0; i < aa.length; i++) + { + if (aa[i] == null || isJalviewOnly(aa[i])) + { + continue; + } + if (aa[i].sequenceRef != null) + { + org.vamsas.objects.core.AlignmentSequence alsref = (org.vamsas. + objects.core.AlignmentSequence) getjv2vObj(aa[i].sequenceRef); + org.vamsas.objects.core.AlignmentSequenceAnnotation an = (org. + vamsas.objects.core.AlignmentSequenceAnnotation) getjv2vObj(aa[ + i]); + int[] gapMap = null; + if (AlSeqMaps.containsKey(aa[i].sequenceRef)) + { + gapMap = (int[]) AlSeqMaps.get(aa[i].sequenceRef); + } + else + { + gapMap = new int[aa[i].sequenceRef.getLength()]; + // map from alignment position to sequence position. + int[] sgapMap = aa[i].sequenceRef.gapMap(); + for (int a = 0; a < sgapMap.length; a++) + { + gapMap[sgapMap[a]] = a; + } + } + if (an == null) + { + an = new org.vamsas.objects.core.AlignmentSequenceAnnotation(); + Seg vSeg = new Seg(); + vSeg.setStart(1); + vSeg.setInclusive(true); + vSeg.setEnd(gapMap.length); + an.addSeg(vSeg); + an.setType("jalview:SecondaryStructurePrediction"); // TODO: better fix this rough guess ;) + alsref.addAlignmentSequenceAnnotation(an); + bindjvvobj(aa[i], an); + // LATER: much of this is verbatim from the alignmentAnnotation + // method below. suggests refactoring to make rangeAnnotation the + // base class + an.setDescription(aa[i].description); + if (aa[i].graph > 0) + { + an.setGraph(true); // aa[i].graph); + } + else + { + an.setGraph(false); + } + an.setLabel(aa[i].label); + an.setProvenance(dummyProvenance()); // get provenance as user + // created, or jnet, or + // something else. + an.setGroup(Integer.toString(aa[i].graphGroup)); // // JBPNote - + // originally we + // were going to + // store + // graphGroup in + // the Jalview + // specific + // bits. + AnnotationElement ae; + for (int a = 0; a < aa[i].annotations.length; a++) + { + if (aa[i].annotations[a] == null) + { + continue; + } + + ae = new AnnotationElement(); + ae.setDescription(aa[i].annotations[a].description); + ae.addGlyph(new Glyph()); + ae.getGlyph(0) + .setContent(aa[i].annotations[a].displayCharacter); // assume + // jax-b + // takes + // care + // of + // utf8 + // translation + if (aa[i].graph != + jalview.datamodel.AlignmentAnnotation.NO_GRAPH) + { + ae.addValue(aa[i].annotations[a].value); + } + ae.setPosition(gapMap[a] + 1); // position w.r.t. AlignmentSequence + // symbols + if (aa[i].annotations[a].secondaryStructure != ' ') + { + // we only write an annotation where it really exists. + Glyph ss = new Glyph(); + ss + .setDict(org.vamsas.objects.utils.GlyphDictionary. + PROTEIN_SS_3STATE); + ss.setContent(String + .valueOf(aa[i].annotations[a]. + secondaryStructure)); + ae.addGlyph(ss); + } + an.addAnnotationElement(ae); + } + } + else + { + // update reference sequence Annotation + if (an.getModifiable()) + { + // verify existing alignment sequence annotation is up to date + System.out.println("update alignment sequence annotation."); + } + else + { + // verify existing alignment sequence annotation is up to date + System.out + .println( + "make new alignment sequence annotation if modification has happened."); + } + } + } + else + { + // add Alignment Annotation + org.vamsas.objects.core.AlignmentAnnotation an = (org.vamsas. + objects.core.AlignmentAnnotation) getjv2vObj(aa[i]); + if (an == null) + { + an = new org.vamsas.objects.core.AlignmentAnnotation(); + an.setType("jalview:AnnotationRow"); + an.setDescription(aa[i].description); + alignment.addAlignmentAnnotation(an); + Seg vSeg = new Seg(); + vSeg.setStart(1); + vSeg.setInclusive(true); + vSeg.setEnd(jal.getWidth()); + an.addSeg(vSeg); + if (aa[i].graph > 0) + { + an.setGraph(true); // aa[i].graph); + } + an.setLabel(aa[i].label); + an.setProvenance(dummyProvenance()); + if (aa[i].graph != aa[i].NO_GRAPH) + { + an.setGroup(Integer.toString(aa[i].graphGroup)); // // JBPNote - + // originally we + // were going to + // store + // graphGroup in + // the Jalview + // specific + // bits. + an.setGraph(true); + } + else + { + an.setGraph(false); + } + AnnotationElement ae; + + for (int a = 0; a < aa[i].annotations.length; a++) + { + if ( (aa[i] == null) || (aa[i].annotations[a] == null)) + { + continue; + } + + ae = new AnnotationElement(); + ae.setDescription(aa[i].annotations[a].description); + ae.addGlyph(new Glyph()); + ae.getGlyph(0) + .setContent(aa[i].annotations[a].displayCharacter); // assume + // jax-b + // takes + // care + // of + // utf8 + // translation + ae.addValue(aa[i].annotations[a].value); + ae.setPosition(a + 1); + if (aa[i].annotations[a].secondaryStructure != ' ') + { + Glyph ss = new Glyph(); + ss + .setDict(org.vamsas.objects.utils.GlyphDictionary. + PROTEIN_SS_3STATE); + ss.setContent(String + .valueOf(aa[i].annotations[a]. + secondaryStructure)); + ae.addGlyph(ss); + } + an.addAnnotationElement(ae); + } + if (aa[i].editable) + { + //an.addProperty(newProperty("jalview:editable", null, "true")); + an.setModifiable(true); + } + if (aa[i].graph != jalview.datamodel.AlignmentAnnotation.NO_GRAPH) + { + an.setGraph(true); + an.setGroup(Integer.toString(aa[i].graphGroup)); + an.addProperty(newProperty("jalview:graphType", null, + ( (aa[i].graph == + jalview.datamodel.AlignmentAnnotation. + BAR_GRAPH) ? "BAR_GRAPH" : + "LINE_GRAPH"))); + + /** and on and on.. + vProperty=new Property(); + vProperty.setName("jalview:graphThreshhold"); + vProperty.setContent(aa[i].threshold); + */ + + } + } + else + { + if (an.getModifiable()) + { + // verify annotation - update (perhaps) + Cache.log.info( + "update alignment sequence annotation. not yet implemented."); + } + else + { + // verify annotation - update (perhaps) + Cache.log.info("updated alignment sequence annotation added."); + } + } + } + } + } + // ///////////////////////////////////////////////////// + + // ////////////////////////////////////////////// + // /SAVE THE TREES + // ///////////////////////////////// + // FIND ANY ASSOCIATED TREES + if (Desktop.desktop != null) + { + javax.swing.JInternalFrame[] frames = Desktop.desktop.getAllFrames(); + + for (int t = 0; t < frames.length; t++) + { + if (frames[t] instanceof TreePanel) + { + TreePanel tp = (TreePanel) frames[t]; + + if (tp.getAlignment() == jal) + { + Tree tree = (Tree) getjv2vObj(tp); + if (tree == null) + { + tree = new Tree(); + bindjvvobj(tp, tree); + tree.setTitle(tp.getTitle()); + Newick newick = new Newick(); + // TODO: translate sequenceI to leaf mappings to vamsas + // references - see tree specification in schema. + newick.setContent(tp.getTree().toString()); + newick.setTitle(tp.getTitle()); + tree.addNewick(newick); + tree.setProvenance(makeTreeProvenance(jal, tp)); + alignment.addTree(tree); + } + else + { + if (tree.getModifiable()) + { + // verify any changes. + System.out.println("Update tree in document."); + } + else + { + System.out + .println("Add modified tree as new tree in document."); + } + } + } + } + } + } + // Store Jalview specific stuff in the Jalview appData + // not implemented in the SimpleDoc interface. + } + + catch (Exception ex) + { + ex.printStackTrace(); + } + + } + + private Property newProperty(String name, String type, String content) + { + Property vProperty = new Property(); + vProperty.setName(name); + if (type != null) + { + vProperty.setType(type); + } + else + { + vProperty.setType("String"); + } + vProperty.setContent(content); + return vProperty; + } + + /** + * correctly create a RangeAnnotation from a jalview sequence feature + * + * @param dsa + * (typically DataSetAnnotations or AlignmentSequenceAnnotation) + * @param feature + * (the feature to be mapped from) + * @return + */ + private RangeAnnotation getDSAnnotationFromJalview(RangeAnnotation dsa, + SequenceFeature feature) + { + dsa.setType(feature.getType()); + Seg vSeg = new Seg(); + vSeg.setStart(feature.getBegin()); + vSeg.setEnd(feature.getEnd()); + vSeg.setInclusive(true); + dsa.addSeg(vSeg); + dsa.setDescription(feature.getDescription()); + dsa.setStatus(feature.getStatus()); + if (feature.links != null && feature.links.size() > 0) + { + for (int i = 0, iSize = feature.links.size(); i < iSize; i++) + { + String link = (String) feature.links.elementAt(i); + int sep = link.indexOf('|'); + if (sep > -1) + { + Link vLink = new Link(); + if (sep > 0) + { + vLink.setContent(link.substring(0, sep - 1)); + } + else + { + vLink.setContent(""); + } + vLink.setHref(link.substring(sep + 1)); // TODO: validate href. + dsa.addLink(vLink); + } + } + } + dsa.setGroup(feature.getFeatureGroup()); + return dsa; + } + + /** + * correctly creates provenance for trees calculated on an alignment by + * jalview. + * + * @param jal + * @param tp + * @return + */ + private Provenance makeTreeProvenance(AlignmentI jal, TreePanel tp) + { + Provenance prov = new Provenance(); + prov.addEntry(new Entry()); + prov.getEntry(0).setAction("imported " + tp.getTitle()); + prov.getEntry(0).setUser(provEntry.getUser()); + prov.getEntry(0).setApp(provEntry.getApp()); + prov.getEntry(0).setDate(provEntry.getDate()); + if (tp.getTree().hasOriginalSequenceData()) + { + Input vInput = new Input(); + // LATER: check to see if tree input data is contained in this alignment - + // or just correctly resolve the tree's seqData to the correct alignment in + // the document. + // vInput.setObjRef(getjv2vObj(jal)); + vInput.setObjRef(getjv2vObj(tp.getViewPort())); + prov.getEntry(0).setAction("created " + tp.getTitle()); + prov.getEntry(0).addInput(vInput); + vInput.setName("jalview:seqdist"); + prov.getEntry(0).addParam(new Param()); + prov.getEntry(0).getParam(0).setName("treeType"); + prov.getEntry(0).getParam(0).setType("utf8"); + prov.getEntry(0).getParam(0).setContent("NJ"); + + int ranges[] = tp.getTree().seqData.getVisibleContigs(); + // VisibleContigs are with respect to alignment coordinates. Still need offsets + int start = tp.getTree().seqData.getAlignmentOrigin(); + for (int r = 0; r < ranges.length; r += 2) + { + Seg visSeg = new Seg(); + visSeg.setStart(1 + start + ranges[r]); + visSeg.setEnd(start + ranges[r + 1]); + visSeg.setInclusive(true); + vInput.addSeg(visSeg); + } + } + return prov; + } + + /** + * + * @param tp + * @return Object[] { AlignmentView, AlignmentI - reference alignment for + * input } + */ + private Object[] recoverInputData(Provenance tp) + { + for (int pe = 0; pe < tp.getEntryCount(); pe++) + { + if (tp.getEntry(pe).getInputCount() > 0) + { + if (tp.getEntry(pe).getInputCount() > 1) + { + Cache.log.warn("Ignoring additional input spec in provenance entry " + + tp.getEntry(pe).toString()); + } + // LATER: deal sensibly with multiple inputs. + Input vInput = tp.getEntry(pe).getInput(0); + if (vInput.getObjRef() instanceof org.vamsas.objects.core.Alignment) + { + // recover an AlignmentView for the input data + AlignViewport javport = (AlignViewport) getvObj2jv( (org.vamsas. + client.Vobject) vInput + .getObjRef()); + jalview.datamodel.AlignmentI jal = javport.getAlignment(); + jalview.datamodel.CigarArray view = javport.getAlignment(). + getCompactAlignment(); + int from = 1, to = jal.getWidth(); + int offset = 0; // deleteRange modifies its frame of reference + for (int r = 0, s = vInput.getSegCount(); r < s; r++) + { + Seg visSeg = vInput.getSeg(r); + int se[] = getSegRange(visSeg, true); // jalview doesn't do bidirection alignments yet. + if (to < se[1]) + { + Cache.log.warn("Ignoring invalid segment in InputData spec."); + } + else + { + if (se[0] > from) + { + view.deleteRange(offset + from - 1, offset + se[0] - 2); + offset -= se[0] - from; + } + from = se[1] + 1; + } + } + if (from < to) + { + view.deleteRange(offset + from - 1, offset + to - 1); // final deletion - TODO: check off by + // one for to + } + return new Object[] + { + new AlignmentView(view), jal}; + } + } + } + Cache.log.debug("Returning null for input data recovery from provenance."); + return null; + } + + /** + * get start 0) + { + DbRef[] dbref = vdseq.getDbRef(); + for (int db = 0; db < dbref.length; db++) + { + jalview.datamodel.DBRefEntry dbr = (jalview.datamodel.DBRefEntry) + getvObj2jv(dbref[db]); + if (dbr == null) + { + // add new dbref + dsseq.addDBRef(dbr = new jalview.datamodel.DBRefEntry + ( + dbref[db].getSource().toString(), + dbref[db].getVersion().toString(), + dbref[db].getAccessionId().toString())); + bindjvvobj(dbr, dbref[db]); + } + } + } + } + + if (newds) + { + SequenceI[] seqs = new SequenceI[dsseqs.size()]; + for (i = 0, iSize = dsseqs.size(); i < iSize; i++) + { + seqs[i] = (SequenceI) dsseqs.elementAt(i); + dsseqs.setElementAt(null, i); + } + jdataset = new jalview.datamodel.Alignment(seqs); + Cache.log.debug("New vamsas dataset imported into jalview."); + bindjvvobj(jdataset, dataset); + } + // //////// + // add any new dataset sequence feature annotations + if (dataset.getDataSetAnnotations() != null) + { + for (int dsa = 0; dsa < dataset.getDataSetAnnotationsCount(); dsa++) + { + DataSetAnnotations dseta = dataset.getDataSetAnnotations(dsa); + SequenceI dsSeq = (SequenceI) getvObj2jv( (Vobject) dseta.getSeqRef()); + if (dsSeq == null) + { + jalview.bin.Cache.log.warn( + "Couldn't resolve jalview sequenceI for dataset object reference " + + ( (Vobject) dataset.getDataSetAnnotations(dsa).getSeqRef()). + getVorbaId().getId()); + } + else + { + if (dseta.getAnnotationElementCount() == 0) + { + jalview.datamodel.SequenceFeature sf = (jalview.datamodel. + SequenceFeature) getvObj2jv(dseta); + if (sf == null) + { + dsSeq.addSequenceFeature(sf = getJalviewSeqFeature(dseta)); + bindjvvobj(sf, dseta); + } + } + else + { + // TODO: deal with alignmentAnnotation style annotation + // appearing on dataset sequences. + // JBPNote: we could just add them to all alignments but + // that may complicate cross references in the jalview + // datamodel + Cache.log.warn("Ignoring dataset annotation with annotationElements. Not yet supported in jalview."); + } + } + } + } + + if (dataset.getAlignmentCount() > 0) + { + // LOAD ALIGNMENTS from DATASET + + for (int al = 0, nal = dataset.getAlignmentCount(); al < nal; al++) + { + org.vamsas.objects.core.Alignment alignment = dataset.getAlignment( + al); + AlignViewport av = (AlignViewport) getvObj2jv(alignment); + jalview.datamodel.AlignmentI jal = null; + if (av != null) + { + jal = av.getAlignment(); + } + iSize = alignment.getAlignmentSequenceCount(); + boolean newal = (jal == null) ? true : false; + Vector newasAnnots = new Vector(); + char gapChar = ' '; // default for new alignments read in from the document + if (jal != null) + { + dsseqs = jal.getSequences(); // for merge/update + gapChar = jal.getGapCharacter(); + } + else + { + dsseqs = new Vector(); + } + char valGapchar = alignment.getGapChar().charAt(0); + for (i = 0; i < iSize; i++) + { + AlignmentSequence valseq = alignment.getAlignmentSequence(i); + jalview.datamodel.SequenceI alseq = (SequenceI) getvObj2jv(valseq); + if (alseq != null) + { + //TODO: upperCase/LowerCase situation here ? do we allow it ? + //if (!alseq.getSequence().equals(valseq.getSequence())) { + // throw new Error("Broken! - mismatch of dataset sequence and jalview internal dataset sequence."); + if (Cache.log.isDebugEnabled()) + { + Cache.log.debug("Updating apparently edited sequence " + + alseq.getName()); + } + // this might go *horribly* wrong + alseq.setSequence(new String(valseq.getSequence()).replace( + valGapchar, gapChar)); + jremain--; + } + else + { + alseq = new jalview.datamodel.Sequence( + valseq.getName(), + valseq.getSequence().replace(valGapchar, gapChar), + valseq.getStart(), + valseq.getEnd()); + + Vobject datsetseq = (Vobject) valseq.getRefid(); + if (datsetseq != null) + { + alseq.setDatasetSequence( (SequenceI) getvObj2jv(datsetseq)); // exceptions if AlignemntSequence reference isn't a simple SequenceI + } + else + { + Cache.log.error( + "Invalid dataset sequence id (null) for alignment sequence " + + valseq.getVorbaId()); + } + bindjvvobj(alseq, valseq); + alseq.setVamsasId(valseq.getVorbaId().getId()); + dsseqs.add(alseq); + } + if (valseq.getAlignmentSequenceAnnotationCount() > 0) + { + AlignmentSequenceAnnotation[] vasannot = valseq. + getAlignmentSequenceAnnotation(); + for (int a = 0; a < vasannot.length; a++) + { + jalview.datamodel.AlignmentAnnotation asa = (jalview. + datamodel.AlignmentAnnotation) getvObj2jv(vasannot[a]); // TODO: 1:many jalview alignment sequence annotations + if (asa == null) + { + int se[] = getBounds(vasannot[a]); + asa = getjAlignmentAnnotation(jal, vasannot[a]); + asa.sequenceRef = alseq; + asa.createSequenceMapping(alseq, alseq.getStart() + se[0], false); // TODO: verify that positions in alseqAnnotation correspond to ungapped residue positions. + bindjvvobj(asa, vasannot[a]); + newasAnnots.add(asa); + } + else + { + // update existing annotation - can do this in place + if (vasannot[a].getModifiable()) + { + Cache.log.info( + "UNIMPLEMENTED: not recovering user modifiable sequence alignment annotation"); + // TODO: should at least replace with new one - otherwise things will break + // basically do this: + // int se[] = getBounds(vasannot[a]); + // asa.update(getjAlignmentAnnotation(jal, vasannot[a])); // update from another annotation object in place. + // asa.createSequenceMapping(alseq, se[0], false); + + } + } + } + } + } + if (jal == null) + { + SequenceI[] seqs = new SequenceI[dsseqs.size()]; + for (i = 0, iSize = dsseqs.size(); i < iSize; i++) + { + seqs[i] = (SequenceI) dsseqs.elementAt(i); + dsseqs.setElementAt(null, i); + } + jal = new jalview.datamodel.Alignment(seqs); + Cache.log.debug("New vamsas alignment imported into jalview " + + alignment.getVorbaId().getId()); + jal.setDataset(jdataset); + } + if (newasAnnots != null && newasAnnots.size() > 0) + { + // Add the new sequence annotations in to the alignment. + for (int an = 0, anSize = newasAnnots.size(); an < anSize; an++) + { + jal.addAnnotation( (AlignmentAnnotation) newasAnnots.elementAt( + an)); + // TODO: check if anything has to be done - like calling adjustForAlignment or something. + newasAnnots.setElementAt(null, an); + } + newasAnnots = null; + } + // ////////////////////////////////////////// + // //LOAD ANNOTATIONS FOR THE ALIGNMENT + // //////////////////////////////////// + if (alignment.getAlignmentAnnotationCount() > 0) + { + org.vamsas.objects.core.AlignmentAnnotation[] an = alignment. + getAlignmentAnnotation(); + + for (int j = 0; j < an.length; j++) + { + jalview.datamodel.AlignmentAnnotation jan = (jalview.datamodel. + AlignmentAnnotation) getvObj2jv(an[j]); + if (jan != null) + { + // update or stay the same. + // TODO: should at least replace with a new one - otherwise things will break + // basically do this: + // jan.update(getjAlignmentAnnotation(jal, an[a])); // update from another annotation object in place. + + Cache.log.debug("update from vamsas alignment annotation to existing jalview alignment annotation."); + if (an[j].getModifiable()) + { + // TODO: user defined annotation is totally mutable... - so load it up or throw away if locally edited. + Cache.log.info( + "NOT IMPLEMENTED - Recovering user-modifiable annotation - yet..."); + } + // TODO: compare annotation element rows + // TODO: compare props. + } + else + { + jan = getjAlignmentAnnotation(jal, an[j]); + jal.addAnnotation(jan); + bindjvvobj(jan, an[j]); + } + } + } + AlignFrame alignFrame; + if (av == null) + { + Cache.log.debug("New alignframe for alignment " + + alignment.getVorbaId()); + // /////////////////////////////// + // construct alignment view + alignFrame = new AlignFrame(jal, AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + av = alignFrame.getViewport(); + String title = alignment.getProvenance().getEntry(alignment. + getProvenance().getEntryCount() - 1).getAction(); + if (alignment.getPropertyCount() > 0) + { + for (int p = 0, pe = alignment.getPropertyCount(); p < pe; p++) + { + if (alignment.getProperty(p).getName().equals( + "jalview:AlTitle")) + { + title = alignment.getProperty(p).getContent(); + } + } + } + // TODO: automatically create meaningful title for a vamsas alignment using its provenance. + jalview.gui.Desktop.addInternalFrame(alignFrame, + title + "(" + alignment.getVorbaId() + ")", + AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + bindjvvobj(av, alignment); + } + else + { + // find the alignFrame for jal. + // TODO: fix this so we retrieve the alignFrame handing av *directly* + alignFrame = getAlignFrameFor(av); + } + // LOAD TREES + // ///////////////////////////////////// + if (alignment.getTreeCount() > 0) + { + + for (int t = 0; t < alignment.getTreeCount(); t++) + { + Tree tree = alignment.getTree(t); + TreePanel tp = (TreePanel) getvObj2jv(tree); + if (tp != null) + { + Cache.log.info( + "Update from vamsas document to alignment associated tree not implemented yet."); + } + else + { + // make a new tree + Object[] idata = this.recoverInputData(tree.getProvenance()); + try + { + AlignmentView inputData = null; + if (idata != null && idata[0] != null) + { + inputData = (AlignmentView) idata[0]; + } + tp = alignFrame.ShowNewickTree( + new jalview.io.NewickFile(tree.getNewick(0).getContent()), + tree.getNewick(0).getTitle() + " (" + tree.getVorbaId() + + ")", inputData, + 600, 500, + t * 20 + 50, t * 20 + 50); + bindjvvobj(tp, tree); + } + catch (Exception e) + { + Cache.log.warn("Problems parsing treefile '" + + tree.getNewick(0).getContent() + "'", e); + } + } + } + } + + } + } + } + } + } + + // bitfields - should be a template in j1.5 + private static int HASSECSTR = 0; + private static int HASVALS = 1; + private static int HASHPHOB = 2; + private static int HASDC = 3; + private static int HASDESCSTR = 4; + private static int HASTWOSTATE = 5; // not used yet. + /** + * parses the AnnotationElements - if they exist - into jalview.datamodel.Annotation[] rows + * Two annotation rows are made if there are distinct annotation for both at 'pos' and 'after pos' at any particular site. + * @param annotation + * @return { boolean[static int constants ], int[ae.length] - map to annotated object frame, jalview.datamodel.Annotation[], jalview.datamodel.Annotation[] (after)} + */ + private Object[] parseRangeAnnotation(org.vamsas.objects.core.RangeAnnotation + annotation) + { + // set these attributes by looking in the annotation to decide what kind of alignment annotation rows will be made + // TODO: potentially we might make several annotation rows from one vamsas alignment annotation. the jv2Vobj binding mechanism + // may not quite cope with this (without binding an array of annotations to a vamsas alignment annotation) + // summary flags saying what we found over the set of annotation rows. + boolean[] AeContent = new boolean[] + { + false, false, false, false, false}; + int[] rangeMap = getMapping(annotation); + jalview.datamodel.Annotation[][] anot = new jalview.datamodel.Annotation[][] + { + new jalview.datamodel.Annotation[rangeMap.length], + new jalview.datamodel.Annotation[rangeMap.length] + }; + boolean mergeable = true; //false if 'after positions cant be placed on same annotation row as positions. + + if (annotation.getAnnotationElementCount() > 0) + { + AnnotationElement ae[] = annotation.getAnnotationElement(); + for (int aa = 0; aa < ae.length; aa++) + { + int pos = ae[aa].getPosition() - 1; // pos counts from 1 to (|seg.start-seg.end|+1) + if (pos >= 0 && pos < rangeMap.length) + { + int row = ae[aa].getAfter() ? 1 : 0; + if (anot[row][pos] != null) + { + // only time this should happen is if the After flag is set. + Cache.log.debug("Ignoring duplicate annotation site at " + pos); + continue; + } + if (anot[1 - row][pos] != null) + { + mergeable = false; + } + String desc = ""; + if (ae[aa].getDescription() != null) + { + desc = ae[aa].getDescription(); + if (desc.length() > 0) + { + // have imported valid description string + AeContent[HASDESCSTR] = true; + } + } + String dc = null; //ae[aa].getDisplayCharacter()==null ? "dc" : ae[aa].getDisplayCharacter(); + String ss = null; //ae[aa].getSecondaryStructure()==null ? "ss" : ae[aa].getSecondaryStructure(); + java.awt.Color colour = null; + if (ae[aa].getGlyphCount() > 0) + { + Glyph[] glyphs = ae[aa].getGlyph(); + for (int g = 0; g < glyphs.length; g++) + { + if (glyphs[g].getDict().equals(org.vamsas.objects.utils. + GlyphDictionary.PROTEIN_SS_3STATE)) + { + ss = glyphs[g].getContent(); + AeContent[HASSECSTR] = true; + } + else if (glyphs[g].getDict().equals(org.vamsas.objects.utils. + GlyphDictionary. + PROTEIN_HD_HYDRO)) + { + Cache.log.debug("ignoring hydrophobicity glyph marker."); + AeContent[HASHPHOB] = true; + char c = (dc = glyphs[g].getContent()).charAt(0); + // dc may get overwritten - but we still set the colour. + colour = new java.awt.Color(c == '+' ? 255 : 0, + c == '.' ? 255 : 0, + c == '-' ? 255 : 0); + + } + else if (glyphs[g].getDict().equals(org.vamsas.objects.utils. + GlyphDictionary.DEFAULT)) + { + dc = glyphs[g].getContent(); + AeContent[HASDC] = true; + } + else + { + Cache.log.debug("Ignoring unknown glyph type " + + glyphs[g].getDict()); + } + } + } + float val = 0; + if (ae[aa].getValueCount() > 0) + { + AeContent[HASVALS] = true; + if (ae[aa].getValueCount() > 1) + { + Cache.log.warn("ignoring additional " + + (ae[aa].getValueCount() - 1) + + "values in annotation element."); + } + val = ae[aa].getValue(0); + } + if (colour == null) + { + anot[row][pos] = new jalview.datamodel.Annotation( (dc != null) ? + dc : "", desc, (ss != null) ? ss.charAt(0) : ' ', val); + } + else + { + anot[row][pos] = new jalview.datamodel.Annotation( (dc != null) ? + dc : "", desc, (ss != null) ? ss.charAt(0) : ' ', val, colour); + } + } + else + { + Cache.log.warn("Ignoring out of bound annotation element " + aa + + " in " + annotation.getVorbaId().getId()); + } + } + // decide on how many annotation rows are needed. + if (mergeable) + { + for (int i = 0; i < anot[0].length; i++) + { + if (anot[1][i] != null) + { + anot[0][i] = anot[1][i]; + anot[0][i].description = anot[0][i].description + " (after)"; + AeContent[HASDESCSTR] = true; // we have valid description string data + anot[1][i] = null; + } + } + anot[1] = null; + } + else + { + for (int i = 0; i < anot[0].length; i++) + { + anot[1][i].description = anot[1][i].description + " (after)"; + } + } + return new Object[] + { + AeContent, rangeMap, anot[0], anot[1]}; + } + else + { + // no annotations to parse. Just return an empty annotationElement[] array. + return new Object[] + { + AeContent, rangeMap, anot[0], anot[1]}; + } + // return null; + } + + /** + * @param jal the jalview alignment to which the annotation will be attached (ideally - freshly updated from corresponding vamsas alignment) + * @param annotation + * @return unbound jalview alignment annotation object. + */ + private jalview.datamodel.AlignmentAnnotation getjAlignmentAnnotation(jalview. + datamodel.AlignmentI jal, + org.vamsas.objects.core.RangeAnnotation annotation) + { + jalview.datamodel.AlignmentAnnotation jan = null; + if (annotation == null) + { + return null; + } + // boolean hasSequenceRef=annotation.getClass().equals(org.vamsas.objects.core.AlignmentSequenceAnnotation.class); + //boolean hasProvenance=hasSequenceRef || (annotation.getClass().equals(org.vamsas.objects.core.AlignmentAnnotation.class)); + /*int se[] = getBounds(annotation); + if (se==null) + se=new int[] {0,jal.getWidth()-1}; + */ + Object[] parsedRangeAnnotation = parseRangeAnnotation(annotation); + String a_label = annotation.getLabel(); + String a_descr = annotation.getDescription(); + if (a_label == null || a_label.length() == 0) + { + a_label = annotation.getType(); + if (a_label.length() == 0) + { + a_label = "Unamed annotation"; + } + } + if (a_descr == null || a_descr.length() == 0) + { + a_descr = "Annotation of type '" + annotation.getType() + "'"; + } + if (parsedRangeAnnotation == null) + { + Cache.log.debug( + "Inserting empty annotation row elements for a whole-alignment annotation."); + + } + else + { + if (parsedRangeAnnotation[3] != null) + { + Cache.log.warn("Ignoring 'After' annotation row in " + + annotation.getVorbaId()); + } + jalview.datamodel.Annotation[] arow = (jalview.datamodel.Annotation[]) + parsedRangeAnnotation[2]; + boolean[] has = (boolean[]) parsedRangeAnnotation[0]; + // VAMSAS: getGraph is only on derived annotation for alignments - in this way its 'odd' - there is already an existing TODO about removing this flag as being redundant + /*if ((annotation.getClass().equals(org.vamsas.objects.core.AlignmentAnnotation.class) && ((org.vamsas.objects.core.AlignmentAnnotation)annotation).getGraph()) + || (hasSequenceRef=true && ((org.vamsas.objects.core.AlignmentSequenceAnnotation)annotation).getGraph())) { + */ + if (has[HASVALS]) + { + // make bounds and automatic description strings for jalview user's benefit (these shouldn't be written back to vamsas document) + boolean first = true; + float min = 0, max = 1; + int lastval = 0; + for (int i = 0; i < arow.length; i++) + { + if (arow[i] != null) + { + if (i - lastval > 1) + { + // do some interpolation *between* points + if (arow[lastval] != null) + { + float interval = arow[i].value - arow[lastval].value; + interval /= i - lastval; + float base = arow[lastval].value; + for (int ip = lastval + 1, np = 0; ip < i; np++, ip++) + { + arow[ip] = new jalview.datamodel.Annotation("", "", ' ', + interval * np + base); + // NB - Interpolated points don't get a tooltip and description. + } + } + } + lastval = i; + // check range - shouldn't we have a min and max property in the annotation object ? + if (first) + { + min = max = arow[i].value; + first = false; + } + else + { + if (arow[i].value < min) + { + min = arow[i].value; + } + else if (arow[i].value > max) + { + max = arow[i].value; + } + } + // make tooltip and display char value + if (!has[HASDESCSTR]) + { + arow[i].description = arow[i].value + ""; + } + if (!has[HASDC]) + { + arow[i].displayCharacter = arow[i].value + ""; + } + } + } + int type = jalview.datamodel.AlignmentAnnotation.LINE_GRAPH; + if (has[HASHPHOB]) + { + type = jalview.datamodel.AlignmentAnnotation.BAR_GRAPH; + } + jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr, arow, + min, max, type); + } + else + { + jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr, arow); + jan.setThreshold(null); + } + if (annotation.getLinkCount() > 0) + { + Cache.log.warn("Ignoring " + annotation.getLinkCount() + + "links added to AlignmentAnnotation."); + } + if (annotation.getModifiable()) + { + jan.editable = true; + } + + if (annotation.getPropertyCount() > 0) + { + // look for special jalview properties + org.vamsas.objects.core.Property[] props = annotation.getProperty(); + for (int p = 0; p < props.length; p++) + { + if (props[p].getName().equalsIgnoreCase("jalview:graphType")) + { + try + { + // probably a jalview annotation graph so recover the visualization hints. + jan.graph = jalview.datamodel.AlignmentAnnotation. + getGraphValueFromString(props[p].getContent()); + } + catch (Exception e) + { + Cache.log.debug( + "Invalid graph type value in jalview:graphType property."); + } + try + { + if (annotation.getGroup() != null && + annotation.getGroup().length() > 0) + { + jan.graphGroup = Integer.parseInt(annotation.getGroup()); + } + } + catch (Exception e) + { + Cache.log.info("UNIMPLEMENTED : Couldn't parse non-integer group value for setting graphGroup correctly."); + } + } + } + } + + return jan; + + } + + return null; + } + + private SequenceFeature getJalviewSeqFeature(RangeAnnotation dseta) + { + int[] se = getBounds(dseta); + SequenceFeature sf = new jalview.datamodel.SequenceFeature(dseta.getType(), + dseta.getDescription(), dseta.getStatus(), se[0], se[1], dseta + .getGroup()); + if (dseta.getLinkCount() > 0) + { + Link[] links = dseta.getLink(); + for (int i = 0; i < links.length; i++) + { + sf.addLink(links[i].getContent() + "|" + links[i].getHref()); + } + } + return sf; + } + + /** + * get real bounds of a RangeType's specification. start and end are an + * inclusive range within which all segments and positions lie. + * TODO: refactor to vamsas utils + * @param dseta + * @return int[] { start, end} + */ + private int[] getBounds(RangeType dseta) + { + if (dseta != null) + { + int[] se = null; + if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0) + { + throw new Error("Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!"); + } + if (dseta.getSegCount() > 0) + { + se = getSegRange(dseta.getSeg(0), true); + for (int s = 1, sSize = dseta.getSegCount(); s < sSize; s++) + { + int nse[] = getSegRange(dseta.getSeg(s), true); + if (se[0] > nse[0]) + { + se[0] = nse[0]; + } + if (se[1] < nse[1]) + { + se[1] = nse[1]; + } + } + } + if (dseta.getPosCount() > 0) + { + // could do a polarity for pos range too. and pass back indication of discontinuities. + int pos = dseta.getPos(0).getI(); + se = new int[] + { + pos, pos}; + for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++) + { + pos = dseta.getPos(p).getI(); + if (se[0] > pos) + { + se[0] = pos; + } + if (se[1] < pos) + { + se[1] = pos; + } + } + } + return se; + } + return null; + } + + /** + * map from a rangeType's internal frame to the referenced object's coordinate frame. + * @param dseta + * @return int [] { ref(pos)...} for all pos in rangeType's frame. + */ + private int[] getMapping(RangeType dseta) + { + Vector posList = new Vector(); + if (dseta != null) + { + int[] se = null; + if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0) + { + throw new Error("Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!"); + } + if (dseta.getSegCount() > 0) + { + for (int s = 0, sSize = dseta.getSegCount(); s < sSize; s++) + { + se = getSegRange(dseta.getSeg(s), false); + int se_end = se[1 - se[2]] + (se[2] == 0 ? 1 : -1); + for (int p = se[se[2]]; p != se_end; p += se[2] == 0 ? 1 : -1) + { + posList.add(new Integer(p)); + } + } + } + else if (dseta.getPosCount() > 0) + { + int pos = dseta.getPos(0).getI(); + + for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++) + { + pos = dseta.getPos(p).getI(); + posList.add(new Integer(pos)); + } + } + } + if (posList != null && posList.size() > 0) + { + int[] range = new int[posList.size()]; + for (int i = 0; i < range.length; i++) + { + range[i] = ( (Integer) posList.elementAt(i)).intValue(); + } + posList.clear(); + return range; + } + return null; + } + + /* not needed now. + * Provenance getVamsasProvenance(jalview.datamodel.Provenance jprov) { + jalview.datamodel.ProvenanceEntry[] entries = null; + // TODO: fix App and Action here. + Provenance prov = new Provenance(); + org.exolab.castor.types.Date date = new org.exolab.castor.types.Date( + new java.util.Date()); + Entry provEntry; + + if (jprov != null) + { + entries = jprov.getEntries(); + for (int i = 0; i < entries.length; i++) + { + provEntry = new Entry(); + try + { + date = new org.exolab.castor.types.Date(entries[i].getDate()); + } catch (Exception ex) + { + ex.printStackTrace(); + + date = new org.exolab.castor.types.Date(entries[i].getDate()); + } + provEntry.setDate(date); + provEntry.setUser(entries[i].getUser()); + provEntry.setAction(entries[i].getAction()); + prov.addEntry(provEntry); + } + } + else + { + provEntry = new Entry(); + provEntry.setDate(date); + provEntry.setUser(System.getProperty("user.name")); // TODO: ext string + provEntry.setApp("JVAPP"); // TODO: ext string + provEntry.setAction(action); + prov.addEntry(provEntry); + } + + return prov; + } + */ + jalview.datamodel.Provenance getJalviewProvenance(Provenance prov) + { + // TODO: fix App and Action entries and check use of provenance in jalview. + jalview.datamodel.Provenance jprov = new jalview.datamodel.Provenance(); + for (int i = 0; i < prov.getEntryCount(); i++) + { + jprov.addEntry(prov.getEntry(i).getUser(), prov.getEntry(i).getAction(), + prov.getEntry(i).getDate().toDate(), + prov.getEntry(i).getId()); + } + + return jprov; + } + + /** + * + * @return default initial provenance list for a Jalview created vamsas + * object. + */ + Provenance dummyProvenance() + { + return dummyProvenance(null); + } + + Entry dummyPEntry(String action) + { + Entry entry = new Entry(); + entry.setApp(this.provEntry.getApp()); + if (action != null) + { + entry.setAction(action); + } + else + { + entry.setAction("created."); + } + entry.setDate(new org.exolab.castor.types.Date(new java.util.Date())); + entry.setUser(this.provEntry.getUser()); + return entry; + } + + Provenance dummyProvenance(String action) + { + Provenance prov = new Provenance(); + prov.addEntry(dummyPEntry(action)); + return prov; + } + + void addProvenance(Provenance p, String action) + { + p.addEntry(dummyPEntry(action)); + } + +} diff --git a/src/jalview/schemes/ScoreMatrix.java b/src/jalview/schemes/ScoreMatrix.java new file mode 100644 index 0000000..69a0922 --- /dev/null +++ b/src/jalview/schemes/ScoreMatrix.java @@ -0,0 +1,67 @@ +package jalview.schemes; + +public class ScoreMatrix +{ + String name; + /** + * reference to integer score matrix + */ + int[][] matrix; + /** + * 0 for Protein Score matrix. 1 for dna score matrix + */ + int type; + ScoreMatrix(String name, int[][] matrix, int type) + { + this.matrix = matrix; + this.type = type; + } + + public boolean isDNA() + { + return type == 1; + } + + public boolean isProtein() + { + return type == 0; + } + + public int[][] getMatrix() + { + return matrix; + } + + /** + * + * @param A1 + * @param A2 + * @return score for substituting first char in A1 with first char in A2 + */ + public int getPairwiseScore(String A1, String A2) + { + return getPairwiseScore(A1.charAt(0), A2.charAt(0)); + } + + public int getPairwiseScore(char c, char d) + { + int pog = 0; + + try + { + int a = (type == 0) ? ResidueProperties.aaIndex[c] : + ResidueProperties.nucleotideIndex[c]; + int b = (type == 0) ? ResidueProperties.aaIndex[d] : + ResidueProperties.nucleotideIndex[d]; + + pog = matrix[a][b]; + } + catch (Exception e) + { + //System.out.println("Unknown residue in " + A1 + " " + A2); + } + + return pog; + } + +} diff --git a/src/jalview/util/MapList.java b/src/jalview/util/MapList.java new file mode 100644 index 0000000..215ece9 --- /dev/null +++ b/src/jalview/util/MapList.java @@ -0,0 +1,395 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer + * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +package jalview.util; + +import java.util.*; + +/** + * MapList + * Simple way of bijectively mapping a non-contiguous linear range to another non-contiguous linear range + * Use at your own risk! + * TODO: efficient implementation of private posMap method + * TODO: test/ensure that sense of from and to ratio start position is conserved (codon start position recovery) + * TODO: optimize to use int[][] arrays rather than vectors. + */ +public class MapList +{ + public Vector fromShifts; + public Vector toShifts; + int fromRatio; // number of steps in fromShifts to one toRatio unit + int toRatio; // number of steps in toShifts to one fromRatio + public MapList(int from[], int to[], int fromRatio, int toRatio) + { + fromShifts = new Vector(); + for (int i = 0; i < from.length; i += 2) + { + fromShifts.add(new int[] + {from[i], from[i + 1]}); + } + toShifts = new Vector(); + for (int i = 0; i < to.length; i += 2) + { + toShifts.add(new int[] + {to[i], to[i + 1]}); + } + this.fromRatio = fromRatio; + this.toRatio = toRatio; + } + + /** + * get all mapped positions from 'from' to 'to' + * @return int[][] { int[] { fromStart, fromFinish, toStart, toFinish }, int [fromFinish-fromStart+2] { toStart..toFinish mappings}} + */ + public int[][] makeFromMap() + { + return posMap(fromShifts, fromRatio, toShifts, toRatio); + } + + /** + * get all mapped positions from 'to' to 'from' + * @return int[to position]=position mapped in from + */ + public int[][] makeToMap() + { + return posMap(toShifts, toRatio, fromShifts, fromRatio); + } + + /** + * construct an int map for intervals in intVals + * @param intVals + * @return int[] { from, to pos in range }, int[range.to-range.from+1] returning mapped position + */ + private int[][] posMap(Vector intVals, int ratio, Vector toIntVals, + int toRatio) + { + Iterator iv = intVals.iterator(); + if (!iv.hasNext()) + { + return null; + } + int[] intv = (int[]) iv.next(); + int from = intv[0], to = intv[1]; + if (from > to) + { + from = intv[1]; + to = intv[0]; + } + while (iv.hasNext()) + { + intv = (int[]) iv.next(); + if (intv[0] < from) + { + from = intv[0]; + } + if (intv[1] < from) + { + from = intv[1]; + } + if (intv[0] > to) + { + to = intv[0]; + } + if (intv[1] > to) + { + to = intv[1]; + } + } + int tF = 0, tT = 0; + int mp[][] = new int[to - from + 2][]; + for (int i = 0; i < mp.length; i++) + { + int[] m = shift(i + from, intVals, ratio, toIntVals, toRatio); + if (m != null) + { + if (i == 0) + { + tF = tT = m[0]; + } + else + { + if (m[0] < tF) + { + tF = m[0]; + } + if (m[0] > tT) + { + tT = m[0]; + } + } + } + mp[i] = m; + } + int[][] map = new int[][] + { + new int[] + { + from, to, tF, tT}, new int[to - from + 2]}; + + map[0][2] = tF; + map[0][3] = tT; + + for (int i = 0; i < mp.length; i++) + { + if (mp[i] != null) + { + map[1][i] = mp[i][0] - tF; + } + else + { + map[1][i] = -1; // indicates an out of range mapping + } + } + return map; + } + + /** + * addShift + * @param pos start position for shift (in original reference frame) + * @param shift length of shift + * + public void addShift(int pos, int shift) + { + int sidx = 0; + int[] rshift=null; + while (sidx= intv[0] && pos <= intv[1]) + { + return new int[] + { + count + pos - intv[0] + 1, +1}; + } + else + { + count += intv[1] - intv[0] + 1; + } + } + else + { + if (pos >= intv[1] && pos <= intv[0]) + { + return new int[] + { + count + intv[0] - pos + 1, -1}; + } + else + { + count += intv[0] - intv[1] + 1; + } + } + } + return null; + } + + /** + * count out pos positions into a series of intervals and return the position + * @param intVals + * @param pos + * @return position pos in interval set + */ + private int[] countToPos(Iterator intVals, int pos) + { + int count = 0, diff = 0, intv[] = + { + 0, 0}; + while (intVals.hasNext()) + { + intv = (int[]) intVals.next(); + diff = intv[1] - intv[0]; + if (diff >= 0) + { + if (pos <= count + 1 + diff) + { + return new int[] + { + pos - count - 1 + intv[0], +1}; + } + else + { + count += 1 + diff; + } + } + else + { + if (pos <= count + 1 - diff) + { + return new int[] + { + intv[0] - (pos - count - 1), -1}; + } + else + { + count += 1 - diff; + } + } + } + return null; //(diff<0) ? (intv[1]-1) : (intv[0]+1); + } + + public static void testMap(MapList ml, int fromS, int fromE) + { + for (int from = 1; from <= 25; from++) + { + int[] too = ml.shiftFrom(from); + System.out.print("ShiftFrom(" + from + ")=="); + if (too == null) + { + System.out.print("NaN\n"); + } + else + { + System.out.print(too[0] + " % " + too[1]); + System.out.print("\t+--+\t"); + int[] toofrom = ml.shiftTo(too[0]); + if (toofrom != null) + { + if (toofrom[0] != from) + { + System.err.println("Mapping not reflexive:" + from + " " + too[0] + + "->" + toofrom[0]); + } + System.out.println("ShiftTo(" + too[0] + ")==" + toofrom[0] + " % " + + toofrom[1]); + } + else + { + System.out.println("ShiftTo(" + too[0] + ")==" + + "NaN! - not Bijective Mapping!"); + } + } + } + int mmap[][] = ml.makeFromMap(); + System.out.println("FromMap : (" + mmap[0][0] + " " + mmap[0][1] + " " + + mmap[0][2] + " " + mmap[0][3] + " "); + for (int i = 1; i <= mmap[1].length; i++) + { + if (mmap[1][i - 1] == -1) + { + System.out.print(i + "=XXX"); + + } + else + { + System.out.print(i + "=" + (mmap[0][2] + mmap[1][i - 1])); + } + if (i % 20 == 0) + { + System.out.print("\n"); + } + else + { + System.out.print(","); + } + } + System.out.print("\n"); + } + + public static void main(String argv[]) + { + MapList ml = new MapList(new int[] + {1, 5, 10, 15, 25, 20}, + new int[] + {51, 1}, 1, 3); + MapList ml1 = new MapList(new int[] + {1, 3, 17, 4}, + new int[] + {51, 1}, 1, 3); + + // test internal consistency + int to[] = new int[51]; + MapList.testMap(ml, 1, 25); + /* + for (int from=1; from<=51; from++) { + int[] too=ml.shiftTo(from); + int[] toofrom=ml.shiftFrom(too[0]); + System.out.println("ShiftFrom("+from+")=="+too[0]+" % "+too[1]+"\t+-+\tShiftTo("+too[0]+")=="+toofrom[0]+" % "+toofrom[1]); + }*/ + System.out.print("Success?\n"); // if we get here - something must be working! + } +} diff --git a/src/jalview/ws/JPredThread.java b/src/jalview/ws/JPredThread.java new file mode 100644 index 0000000..0cac709 --- /dev/null +++ b/src/jalview/ws/JPredThread.java @@ -0,0 +1,634 @@ +package jalview.ws; + +import java.util.*; + +import jalview.analysis.*; +import jalview.bin.*; +import jalview.datamodel.*; +import jalview.datamodel.Alignment; +import jalview.gui.*; +import jalview.io.*; +import jalview.util.*; +import vamsas.objects.simple.JpredResult; + +class JPredThread + extends WSThread implements WSClientI +{ + // TODO: put mapping between JPredJob input and input data here - JNetAnnotation adding is done after result parsing. + class JPredJob + extends WSThread.WSJob + { + // TODO: make JPredJob deal only with what was sent to and received from a JNet service + int[] predMap = null; // mapping from sequence(i) to the original sequence(predMap[i]) being predicted on + vamsas.objects.simple.Sequence sequence; + vamsas.objects.simple.Msfalignment msa; + java.util.Hashtable SequenceInfo = null; + int msaIndex = 0; // the position of the original sequence in the array of Sequences in the input object that this job holds a prediction for + /** + * + * @return true if getResultSet will return a valid alignment and prediction result. + */ + public boolean hasResults() + { + if (subjobComplete && result != null && result.isFinished() + && ( (JpredResult) result).getPredfile() != null && + ( (JpredResult) result).getAligfile() != null) + { + return true; + } + return false; + } + + boolean hasValidInput() + { + if (sequence != null) + { + return true; + } + return false; + } + + /** + * + * @return null or Object[] { annotated alignment for this prediction, ColumnSelection for this prediction} or null if no results available. + * @throws Exception + */ + public Object[] getResultSet() + throws Exception + { + if (result == null || !result.isFinished()) + { + return null; + } + Alignment al = null; + ColumnSelection alcsel = null; + int FirstSeq = -1; // the position of the query sequence in Alignment al + + JpredResult result = (JpredResult)this.result; + + jalview.bin.Cache.log.debug("Parsing output from JNet job."); + // JPredFile prediction = new JPredFile("C:/JalviewX/files/jpred.txt", "File"); + jalview.io.JPredFile prediction = new jalview.io.JPredFile(result. + getPredfile(), + "Paste"); + SequenceI[] preds = prediction.getSeqsAsArray(); + jalview.bin.Cache.log.debug("Got prediction profile."); + + if ( (this.msa != null) && (result.getAligfile() != null)) + { + jalview.bin.Cache.log.debug("Getting associated alignment."); + // we ignore the returned alignment if we only predicted on a single sequence + String format = new jalview.io.IdentifyFile().Identify(result. + getAligfile(), + "Paste"); + + if (jalview.io.FormatAdapter.isValidFormat(format)) + { + SequenceI sqs[]; + if (predMap != null) + { + Object[] alandcolsel = input.getAlignmentAndColumnSelection( + alignFrame.getViewport().getGapCharacter()); + sqs = (SequenceI[]) alandcolsel[0]; + al = new Alignment(sqs); + alcsel = (ColumnSelection) alandcolsel[1]; + } + else + { + al = new FormatAdapter().readFile(result.getAligfile(), + "Paste", format); + sqs = new SequenceI[al.getHeight()]; + + for (int i = 0, j = al.getHeight(); i < j; i++) + { + sqs[i] = al.getSequenceAt(i); + } + if (!jalview.analysis.SeqsetUtils.deuniquify( (Hashtable) + SequenceInfo, sqs)) + { + throw (new Exception( + "Couldn't recover sequence properties for alignment.")); + } + } + FirstSeq = 0; + al.setDataset(null); + + jalview.io.JnetAnnotationMaker.add_annotation(prediction, al, + FirstSeq, + false, predMap); + + } + else + { + throw (new Exception( + "Unknown format " + format + " for file : \n" + + result.getAligfile())); + } + } + else + { + al = new Alignment(preds); + FirstSeq = prediction.getQuerySeqPosition(); + if (predMap != null) + { + char gc = alignFrame.getViewport().getGapCharacter(); + SequenceI[] sqs = (SequenceI[]) ( (java.lang.Object[]) input. + getAlignmentAndColumnSelection(gc))[ + 0]; + if (this.msaIndex >= sqs.length) + { + throw new Error("Implementation Error! Invalid msaIndex for JPredJob on parent MSA input object!"); + } + + ///// + //Uses RemoveGapsCommand + ///// + new jalview.commands.RemoveGapsCommand("Remove Gaps", + new SequenceI[] + {sqs[msaIndex]}, + alignFrame.getCurrentView(). + getAlignment()); + + SequenceI profileseq = al.getSequenceAt(FirstSeq); + profileseq.setSequence(sqs[msaIndex].getSequenceAsString()); + } + + if (!jalview.analysis.SeqsetUtils.SeqCharacterUnhash( + al.getSequenceAt(FirstSeq), SequenceInfo)) + { + throw (new Exception( + "Couldn't recover sequence properties for JNet Query sequence!")); + } + else + { + al.setDataset(null); + jalview.io.JnetAnnotationMaker.add_annotation(prediction, al, + FirstSeq, + true, predMap); + SequenceI profileseq = al.getSequenceAt(0); // this includes any gaps. + alignToProfileSeq(al, profileseq); + if (predMap != null) + { + // Adjust input view for gaps + // propagate insertions into profile + alcsel = propagateInsertions(profileseq, al, input); + } + } + } + return new Object[] + { + al, alcsel}; // , FirstSeq, noMsa}; + } + + /** + * Given an alignment where all other sequences except profileseq are aligned to the ungapped profileseq, insert gaps in the other sequences to realign them with the residues in profileseq + * @param al + * @param profileseq + */ + private void alignToProfileSeq(Alignment al, SequenceI profileseq) + { + char gc = al.getGapCharacter(); + int[] gapMap = profileseq.gapMap(); + // insert gaps into profile + for (int lp = 0, r = 0; r < gapMap.length; r++) + { + if (gapMap[r] - lp > 1) + { + StringBuffer sb = new StringBuffer(); + for (int s = 0, ns = gapMap[r] - lp; s < ns; s++) + { + sb.append(gc); + } + for (int s = 1, ns = al.getHeight(); s < ns; s++) + { + String sq = al.getSequenceAt(s).getSequenceAsString(); + int diff = gapMap[r] - sq.length(); + if (diff > 0) + { + // pad gaps + sq = sq + sb; + while ( (diff = gapMap[r] - sq.length()) > 0) + { + sq = sq + + ( (diff >= sb.length()) ? sb.toString() : + sb.substring(0, diff)); + } + al.getSequenceAt(s).setSequence(sq); + } + else + { + al.getSequenceAt(s).setSequence(sq.substring(0, gapMap[r]) + + sb.toString() + + sq.substring(gapMap[r])); + } + } + } + lp = gapMap[r]; + } + } + + /** + * Add gaps into the sequences aligned to profileseq under the given AlignmentView + * @param profileseq + * @param al + * @param input + */ + private ColumnSelection propagateInsertions(SequenceI profileseq, + Alignment al, + AlignmentView input) + { + char gc = al.getGapCharacter(); + Object[] alandcolsel = input.getAlignmentAndColumnSelection(gc); + ColumnSelection nview = (ColumnSelection) alandcolsel[1]; + SequenceI origseq; + nview.pruneDeletions(ShiftList.parseMap( (origseq = ( (SequenceI[]) + alandcolsel[0])[0]).gapMap())); // recover original prediction sequence's mapping to view. + int[] viscontigs = nview.getVisibleContigs(0, profileseq.getLength()); + int spos = 0; + int offset = 0; + // input.pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap())) + // add profile to visible contigs + for (int v = 0; v < viscontigs.length; v += 2) + { + if (viscontigs[v] > spos) + { + StringBuffer sb = new StringBuffer(); + for (int s = 0, ns = viscontigs[v] - spos; s < ns; s++) + { + sb.append(gc); + } + for (int s = 0, ns = al.getHeight(); s < ns; s++) + { + SequenceI sqobj = al.getSequenceAt(s); + if (sqobj != profileseq) + { + String sq = al.getSequenceAt(s).getSequenceAsString(); + if (sq.length() <= spos + offset) + { + // pad sequence + int diff = spos + offset - sq.length() - 1; + if (diff > 0) + { + // pad gaps + sq = sq + sb; + while ( (diff = spos + offset - sq.length() - 1) > 0) + { + sq = sq + + ( (diff >= sb.length()) ? sb.toString() : + sb.substring(0, diff)); + } + } + sq += sb.toString(); + } + else + { + al.getSequenceAt(s).setSequence(sq.substring(0, spos + offset) + + sb.toString() + + sq.substring(spos + offset)); + } + } + } + //offset+=sb.length(); + } + spos = viscontigs[v + 1] + 1; + } + if ( (offset + spos) < profileseq.getLength()) + { + StringBuffer sb = new StringBuffer(); + for (int s = 0, ns = profileseq.getLength() - spos - offset; s < ns; s++) + { + sb.append(gc); + } + for (int s = 1, ns = al.getHeight(); s < ns; s++) + { + String sq = al.getSequenceAt(s).getSequenceAsString(); + // pad sequence + int diff = origseq.getLength() - sq.length(); + while (diff > 0) + { + sq = sq + + ( (diff >= sb.length()) ? sb.toString() : sb.substring(0, diff)); + diff = origseq.getLength() - sq.length(); + } + } + } + return nview; + } + + public JPredJob(Hashtable SequenceInfo, SequenceI seq, int[] delMap) + { + super(); + this.predMap = delMap; + String sq = AlignSeq.extractGaps(Comparison.GapChars, + seq.getSequenceAsString()); + if (sq.length() >= 20) + { + this.SequenceInfo = SequenceInfo; + sequence = new vamsas.objects.simple.Sequence(); + sequence.setId(seq.getName()); + sequence.setSeq(sq); + } + } + + public JPredJob(Hashtable SequenceInfo, SequenceI[] msf, int[] delMap) + { + this(SequenceInfo, msf[0], delMap); + if (sequence != null) + { + if (msf.length > 1) + { + msa = new vamsas.objects.simple.Msfalignment(); + jalview.io.PileUpfile pileup = new jalview.io.PileUpfile(); + msa.setMsf(pileup.print(msf)); + } + } + } + } + + ext.vamsas.Jpred server; + String altitle = ""; + JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, + String wsurl, AlignmentView alview, AlignFrame alframe) + { + super(); + this.altitle = altitle; + this.server = server; + this.wsInfo = wsinfo; + this.input = alview; + this.alignFrame = alframe; + WsUrl = wsurl; + } + + JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, + String wsurl, Hashtable SequenceInfo, SequenceI seq, int[] delMap, + AlignmentView alview, AlignFrame alframe) + { + this(wsinfo, altitle, server, wsurl, alview, alframe); + JPredJob job = new JPredJob(SequenceInfo, seq, delMap); + if (job.hasValidInput()) + { + OutputHeader = wsInfo.getProgressText(); + jobs = new WSJob[] + { + job}; + job.jobnum = 0; + } + } + + JPredThread(WebserviceInfo wsinfo, String altitle, ext.vamsas.Jpred server, + Hashtable SequenceInfo, SequenceI[] msf, int[] delMap, + AlignmentView alview, AlignFrame alframe, String wsurl) + { + this(wsinfo, altitle, server, wsurl, alview, alframe); + JPredJob job = new JPredJob(SequenceInfo, msf, delMap); + if (job.hasValidInput()) + { + jobs = new WSJob[] + { + job}; + OutputHeader = wsInfo.getProgressText(); + job.jobnum = 0; + } + } + + void StartJob(WSJob j) + { + if (! (j instanceof JPredJob)) + { + throw new Error("Implementation error - StartJob(JpredJob) called on " + + j.getClass()); + } + try + { + JPredJob job = (JPredJob) j; + if (job.msa != null) + { + job.jobId = server.predictOnMsa(job.msa); + } + else + if (job.sequence != null) + { + job.jobId = server.predict(job.sequence); // debug like : job.jobId = "/jobs/www-jpred/jp_Yatat29";// + } + + if (job.jobId != null) + { + if (job.jobId.startsWith("Broken")) + { + job.result = (vamsas.objects.simple.Result)new JpredResult(); + job.result.setInvalid(true); + job.result.setStatus("Submission " + job.jobId); + } + else + { + job.submitted = true; + job.subjobComplete = false; + Cache.log.info(WsUrl + " Job Id '" + job.jobId + "'"); + } + } + else + { + throw new Exception("Server timed out - try again later\n"); + } + } + catch (Exception e) + { + if (e.getMessage().indexOf("Exception") > -1) + { + wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_SERVERERROR); + wsInfo.setProgressText(j.jobnum, + "Failed to submit the prediction. (Just close the window)\n" + + + "It is most likely that there is a problem with the server.\n"); + System.err.println( + "JPredWS Client: Failed to submit the prediction. Quite possibly because of a server error - see below)\n" + + e.getMessage() + "\n"); + + jalview.bin.Cache.log.warn("Server Exception", e); + } + else + { + wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR); + // JBPNote - this could be a popup informing the user of the problem. + wsInfo.appendProgressText(j.jobnum, + "Failed to submit the prediction:\n" + + e.getMessage() + + wsInfo.getProgressText()); + + jalview.bin.Cache.log.debug("Failed Submission of job " + j.jobnum, e); + + } + j.allowedServerExceptions = -1; + j.subjobComplete = true; + } + } + + void parseResult() + { + int results = 0; // number of result sets received + JobStateSummary finalState = new JobStateSummary(); + try + { + for (int j = 0; j < jobs.length; j++) + { + finalState.updateJobPanelState(wsInfo, OutputHeader, jobs[j]); + if (jobs[j].submitted && jobs[j].subjobComplete && jobs[j].hasResults()) + { + results++; + } + } + } + catch (Exception ex) + { + + Cache.log.error("Unexpected exception when processing results for " + + altitle, ex); + wsInfo.setStatus(WebserviceInfo.STATE_STOPPED_ERROR); + } + if (results > 0) + { + wsInfo.showResultsNewFrame + .addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed( + java.awt.event.ActionEvent evt) + { + displayResults(true); + } + }); + wsInfo.mergeResults + .addActionListener(new java.awt.event.ActionListener() + { + public void actionPerformed( + java.awt.event.ActionEvent evt) + { + displayResults(false); + } + }); + wsInfo.setResultsReady(); + } + else + { + wsInfo.setFinishedNoResults(); + } + } + + void displayResults(boolean newWindow) + { + // TODO: cope with multiple subjobs. + if (jobs != null) + { + Object[] res = null; + boolean msa = false; + for (int jn = 0; jn < jobs.length; jn++) + { + Object[] jobres = null; + JPredJob j = (JPredJob) jobs[jn]; + + if (j.hasResults()) + { + // hack - we only deal with all single seuqence predictions or all profile predictions + msa = (j.msa != null) ? true : msa; + try + { + jalview.bin.Cache.log.debug("Parsing output of job " + jn); + jobres = j.getResultSet(); + jalview.bin.Cache.log.debug("Finished parsing output."); + if (jobs.length == 1) + { + res = jobres; + } + else + { + // do merge with other job results + throw new Error( + "Multiple JNet subjob merging not yet implemented."); + } + } + catch (Exception e) + { + jalview.bin.Cache.log.error( + "JNet Client: JPred Annotation Parse Error", + e); + wsInfo.setStatus(j.jobnum, WebserviceInfo.STATE_STOPPED_ERROR); + wsInfo.appendProgressText(j.jobnum, + OutputHeader + "\n" + + j.result.getStatus() + + "\nInvalid JNet job result data!\n" + + e.getMessage()); + j.result.setBroken(true); + } + } + } + + if (res != null) + { + if (newWindow) + { + AlignFrame af; + if (input == null) + { + if (res[1] != null) + { + af = new AlignFrame( (Alignment) res[0], (ColumnSelection) res[1], + AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + } + else + { + af = new AlignFrame( (Alignment) res[0], AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + } + } + else + { + /*java.lang.Object[] alandcolsel = input.getAlignmentAndColumnSelection(alignFrame.getViewport().getGapCharacter()); + if (((SequenceI[])alandcolsel[0])[0].getLength()!=res.getWidth()) { + if (msa) { + throw new Error("Implementation Error! ColumnSelection from input alignment will not map to result alignment!"); + } + } + if (!msa) { + // update hidden regions to account for loss of gaps in profile. - if any + // gapMap returns insert list, interpreted as delete list by pruneDeletions + //((ColumnSelection) alandcolsel[1]).pruneDeletions(ShiftList.parseMap(((SequenceI[]) alandcolsel[0])[0].gapMap())); + }*/ + + af = new AlignFrame( (Alignment) res[0], (ColumnSelection) res[1], + AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + } + Desktop.addInternalFrame(af, altitle, + AlignFrame.DEFAULT_WIDTH, + AlignFrame.DEFAULT_HEIGHT); + } + else + { + Cache.log.info("Append results onto existing alignment."); + } + } + } + } + + void pollJob(WSJob job) + throws Exception + { + job.result = server.getresult(job.jobId); + } + + public boolean isCancellable() + { + return false; + } + + public void cancelJob() + { + throw new Error("Implementation error!"); + } + + public boolean canMergeResults() + { + return false; + } + +}