This section is optional, and allows one or more filters to be defined for each feature type.
+
Only features that satisfy the filter conditions will be displayed.
+
Begin with a line which is just STARTFILTERS, and end with a line which is just ENDFILTERS.
+
Each line has the format:
+
The remaining lines in the file are the sequence annotation
definitions, where the now defined features are attached to regions
on particular sequences. Each feature can optionally include some
descriptive text which is displayed in a tooltip when the mouse is
- near the feature on that sequence (and can also be used to generate
- a colour the feature).
+ near the feature on that sequence (and may also be used to generate
+ a colour for the feature).
diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties
index 449c9b6..43e055d 100644
--- a/resources/lang/Messages.properties
+++ b/resources/lang/Messages.properties
@@ -415,7 +415,7 @@ label.input_alignment_from_url = Input Alignment From URL
label.input_alignment = Input Alignment
label.couldnt_import_as_vamsas_session = Couldn't import {0} as a new vamsas session.
label.vamsas_document_import_failed = Vamsas Document Import Failed
-label.couldnt_locate = Couldn't locate {0}
+label.couldnt_locate = Could not locate {0}
label.url_not_found = URL not found
label.new_sequence_url_link = New sequence URL link
label.cannot_edit_annotations_in_wrapped_view = Cannot edit annotations in wrapped view
@@ -1282,7 +1282,6 @@ label.SEQUENCE_ID_for_DB_ACCESSION1 = Please review your URL links in the 'Conne
label.SEQUENCE_ID_for_DB_ACCESSION2 = URL links using '$SEQUENCE_ID$' for DB accessions now use '$DB_ACCESSION$'.
label.do_not_display_again = Do not display this message again
exception.url_cannot_have_duplicate_id = {0} cannot be used as a label for more than one line
-label.filter = Filter text:
action.customfilter = Custom only
action.showall = Show All
label.insert = Insert:
@@ -1350,7 +1349,6 @@ label.colour_by_text = Colour by text
label.graduated_colour = Graduated Colour
label.by_text_of = By text of
label.by_range_of = By range of
-label.filters_tooltip = Click to set or amend filters
label.or = Or
label.and = And
label.sequence_feature_colours = Sequence Feature Colours
@@ -1361,3 +1359,5 @@ label.most_bound_molecules = Most Bound Molecules
label.most_polymer_residues = Most Polymer Residues
label.cached_structures = Cached Structures
label.free_text_search = Free Text Search
+label.configuration = Configuration
+label.configure_feature_tooltip = Click to configure variable colour or filters
diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties
index 3c82386..5aed0cb 100644
--- a/resources/lang/Messages_es.properties
+++ b/resources/lang/Messages_es.properties
@@ -1283,7 +1283,6 @@ label.SEQUENCE_ID_for_DB_ACCESSION1 = Por favor, revise sus URLs en la pesta
label.SEQUENCE_ID_for_DB_ACCESSION2 = URL enlaza usando '$SEQUENCE_ID$' para accesiones DB ahora usar '$DB_ACCESSION$'.
label.do_not_display_again = No mostrar este mensaje de nuevo
exception.url_cannot_have_duplicate_id = {0} no puede ser usada como etiqueta en más de un enlace
-label.filter = Filtrar texto:
action.customfilter = Sólo personalizado
action.showall = Mostrar todo
label.insert = Insertar:
@@ -1351,7 +1350,6 @@ label.colour_by_text = Colorear por texto
label.graduated_colour = Color graduado
label.by_text_of = Por texto de
label.by_range_of = Por rango de
-label.filters_tooltip = Haga clic para configurar o modificar los filtros
label.or = O
label.and = Y
label.sequence_feature_colours = Colores de características de las secuencias
@@ -1362,3 +1360,5 @@ label.most_bound_molecules = M
label.most_polymer_residues = Más Residuos de Polímeros
label.cached_structures = Estructuras en Caché
label.free_text_search = Búsqueda de texto libre
+label.configuration = Configuración
+label.configure_feature_tooltip = Haga clic para configurar el color o los filtros
diff --git a/src/jalview/api/FeatureRenderer.java b/src/jalview/api/FeatureRenderer.java
index cf3c8da..868f196 100644
--- a/src/jalview/api/FeatureRenderer.java
+++ b/src/jalview/api/FeatureRenderer.java
@@ -255,11 +255,11 @@ public interface FeatureRenderer
*
* Returns null if
*
- * - feature type is not visible, or
* - feature group is not visible, or
* - feature values lie outside any colour threshold, or
* - feature is excluded by filter conditions
*
+ * This method does not check feature type visibility.
*
* @param feature
* @return
diff --git a/src/jalview/appletgui/AppletJmolBinding.java b/src/jalview/appletgui/AppletJmolBinding.java
index 2f61b24..e5767b6 100644
--- a/src/jalview/appletgui/AppletJmolBinding.java
+++ b/src/jalview/appletgui/AppletJmolBinding.java
@@ -24,7 +24,6 @@ import jalview.api.AlignmentViewPanel;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
import jalview.ext.jmol.JalviewJmolBinding;
-import jalview.gui.IProgressIndicator;
import jalview.io.DataSourceType;
import jalview.structure.StructureSelectionManager;
@@ -174,21 +173,12 @@ class AppletJmolBinding extends JalviewJmolBinding
@Override
public int[] resizeInnerPanel(String data)
{
- // TODO Auto-generated method stub
return null;
}
@Override
public Map getJSpecViewProperty(String arg0)
{
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- protected IProgressIndicator getIProgressIndicator()
- {
- // no progress indicators on the applet
return null;
}
}
diff --git a/src/jalview/appletgui/ExtJmol.java b/src/jalview/appletgui/ExtJmol.java
index 89228d5..b0d3f7a 100644
--- a/src/jalview/appletgui/ExtJmol.java
+++ b/src/jalview/appletgui/ExtJmol.java
@@ -26,7 +26,6 @@ import jalview.api.SequenceRenderer;
import jalview.datamodel.PDBEntry;
import jalview.datamodel.SequenceI;
import jalview.ext.jmol.JalviewJmolBinding;
-import jalview.gui.IProgressIndicator;
import jalview.io.DataSourceType;
import java.awt.Container;
@@ -66,18 +65,8 @@ public class ExtJmol extends JalviewJmolBinding
}
@Override
- protected IProgressIndicator getIProgressIndicator()
- {
- // no progress indicators on applet (could access javascript for this)
- return null;
- }
-
- @Override
public void updateColours(Object source)
{
-
- // TODO Auto-generated method stub
-
}
@Override
@@ -190,7 +179,6 @@ public class ExtJmol extends JalviewJmolBinding
protected JmolAppConsoleInterface createJmolConsole(
Container consolePanel, String buttonsToShow)
{
- // TODO Auto-generated method stub
return null;
}
@@ -205,14 +193,11 @@ public class ExtJmol extends JalviewJmolBinding
@Override
public void releaseReferences(Object svl)
{
- // TODO Auto-generated method stub
-
}
@Override
public Map getJSpecViewProperty(String arg0)
{
- // TODO Auto-generated method stub
return null;
}
diff --git a/src/jalview/appletgui/OverviewCanvas.java b/src/jalview/appletgui/OverviewCanvas.java
index e99c021..07f5919 100644
--- a/src/jalview/appletgui/OverviewCanvas.java
+++ b/src/jalview/appletgui/OverviewCanvas.java
@@ -162,4 +162,12 @@ public class OverviewCanvas extends Component
}
}
+ /**
+ * Nulls references to protect against potential memory leaks
+ */
+ void dispose()
+ {
+ od = null;
+ }
+
}
diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java
index 3bbbe95..5081509 100755
--- a/src/jalview/appletgui/OverviewPanel.java
+++ b/src/jalview/appletgui/OverviewPanel.java
@@ -334,6 +334,10 @@ public class OverviewPanel extends Panel implements Runnable,
} finally
{
av = null;
+ if (oviewCanvas != null)
+ {
+ oviewCanvas.dispose();
+ }
oviewCanvas = null;
ap = null;
od = null;
diff --git a/src/jalview/commands/EditCommand.java b/src/jalview/commands/EditCommand.java
index cac843f..1a227c5 100644
--- a/src/jalview/commands/EditCommand.java
+++ b/src/jalview/commands/EditCommand.java
@@ -20,12 +20,16 @@
*/
package jalview.commands;
+import jalview.analysis.AlignSeq;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.Annotation;
+import jalview.datamodel.Range;
import jalview.datamodel.Sequence;
import jalview.datamodel.SequenceFeature;
import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.SequenceFeaturesI;
+import jalview.util.Comparison;
import jalview.util.ReverseListIterator;
import jalview.util.StringUtils;
@@ -114,7 +118,7 @@ public class EditCommand implements CommandI
public abstract Action getUndoAction();
};
- private List edits = new ArrayList();
+ private List edits = new ArrayList<>();
String description;
@@ -330,20 +334,8 @@ public class EditCommand implements CommandI
int position, int number, AlignmentI al, boolean performEdit,
AlignmentI[] views)
{
- Edit edit = new Edit(command, seqs, position, number,
- al.getGapCharacter());
- if (al.getHeight() == seqs.length)
- {
- edit.al = al;
- edit.fullAlignmentHeight = true;
- }
-
- addEdit(edit);
-
- if (performEdit)
- {
- performEdit(edit, views);
- }
+ Edit edit = new Edit(command, seqs, position, number, al);
+ appendEdit(edit, al, performEdit, views);
}
/**
@@ -534,39 +526,62 @@ public class EditCommand implements CommandI
command.string[i] = sequence.getSequence(command.position,
command.position + command.number);
SequenceI oldds = sequence.getDatasetSequence();
- if (command.oldds != null && command.oldds[i] != null)
- {
- // we are redoing an undone cut.
- sequence.setDatasetSequence(null);
- }
- sequence.deleteChars(command.position,
+ Range cutPositions = sequence.findPositions(command.position + 1,
command.position + command.number);
+ boolean cutIsInternal = cutPositions != null
+ && sequence.getStart() != cutPositions
+ .getBegin() && sequence.getEnd() != cutPositions.getEnd();
+
+ /*
+ * perform the cut; if this results in a new dataset sequence, add
+ * that to the alignment dataset
+ */
+ SequenceI ds = sequence.getDatasetSequence();
+ sequence.deleteChars(command.position, command.position
+ + command.number);
+
if (command.oldds != null && command.oldds[i] != null)
{
- // oldds entry contains the cut dataset sequence.
+ /*
+ * we are Redoing a Cut, or Undoing a Paste - so
+ * oldds entry contains the cut dataset sequence,
+ * with sequence features in expected place
+ */
sequence.setDatasetSequence(command.oldds[i]);
command.oldds[i] = oldds;
}
else
{
- // modify the oldds if necessary
+ /*
+ * new cut operation: save the dataset sequence
+ * so it can be restored in an Undo
+ */
+ if (command.oldds == null)
+ {
+ command.oldds = new SequenceI[command.seqs.length];
+ }
+ command.oldds[i] = oldds;// todo not if !cutIsInternal?
+
+ // do we need to edit sequence features for new sequence ?
if (oldds != sequence.getDatasetSequence()
- || sequence.getFeatures().hasFeatures())
+ || (cutIsInternal
+ && sequence.getFeatures().hasFeatures()))
+ // todo or just test cutIsInternal && cutPositions != null ?
{
- if (command.oldds == null)
+ if (cutPositions != null)
{
- command.oldds = new SequenceI[command.seqs.length];
+ cutFeatures(command, sequence, cutPositions.getBegin(),
+ cutPositions.getEnd(), cutIsInternal);
}
- command.oldds[i] = oldds;
- // FIXME JAL-2541 JAL-2526 get correct positions if on a gap
- adjustFeatures(
- command,
- i,
- sequence.findPosition(command.position),
- sequence.findPosition(command.position + command.number),
- false);
}
}
+ SequenceI newDs = sequence.getDatasetSequence();
+ if (newDs != ds && command.al != null
+ && command.al.getDataset() != null
+ && !command.al.getDataset().getSequences().contains(newDs))
+ {
+ command.al.getDataset().addSequence(newDs);
+ }
}
if (sequence.getLength() < 1)
@@ -588,21 +603,19 @@ public class EditCommand implements CommandI
*/
static void paste(Edit command, AlignmentI[] views)
{
- StringBuffer tmp;
- boolean newDSNeeded;
- boolean newDSWasNeeded;
- int newstart, newend;
boolean seqWasDeleted = false;
- int start = 0, end = 0;
for (int i = 0; i < command.seqs.length; i++)
{
- newDSNeeded = false;
- newDSWasNeeded = command.oldds != null && command.oldds[i] != null;
- if (command.seqs[i].getLength() < 1)
+ boolean newDSNeeded = false;
+ boolean newDSWasNeeded = command.oldds != null
+ && command.oldds[i] != null;
+ SequenceI sequence = command.seqs[i];
+ if (sequence.getLength() < 1)
{
- // ie this sequence was deleted, we need to
- // readd it to the alignment
+ /*
+ * sequence was deleted; re-add it to the alignment
+ */
if (command.alIndex[i] < command.al.getHeight())
{
List sequences;
@@ -610,68 +623,78 @@ public class EditCommand implements CommandI
{
if (!(command.alIndex[i] < 0))
{
- sequences.add(command.alIndex[i], command.seqs[i]);
+ sequences.add(command.alIndex[i], sequence);
}
}
}
else
{
- command.al.addSequence(command.seqs[i]);
+ command.al.addSequence(sequence);
}
seqWasDeleted = true;
}
- newstart = command.seqs[i].getStart();
- newend = command.seqs[i].getEnd();
+ int newStart = sequence.getStart();
+ int newEnd = sequence.getEnd();
- tmp = new StringBuffer();
- tmp.append(command.seqs[i].getSequence());
+ StringBuilder tmp = new StringBuilder();
+ tmp.append(sequence.getSequence());
// Undo of a delete does not replace original dataset sequence on to
// alignment sequence.
+ int start = 0;
+ int length = 0;
+
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)
+ int len = command.position - tmp.length();
+ while (len > 0)
{
tmp.append(command.gapChar);
- length--;
+ len--;
}
}
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)
+ if (!Comparison.isGap(command.string[i][s]))
{
+ length++;
if (!newDSNeeded)
{
newDSNeeded = true;
- start = command.seqs[i].findPosition(command.position);
- end = command.seqs[i]
- .findPosition(command.position + command.number);
+ start = sequence.findPosition(command.position);
+ // end = sequence
+ // .findPosition(command.position + command.number);
}
- if (command.seqs[i].getStart() == start)
+ if (sequence.getStart() == start)
{
- newstart--;
+ newStart--;
}
else
{
- newend++;
+ newEnd++;
}
}
}
command.string[i] = null;
}
- command.seqs[i].setSequence(tmp.toString());
- command.seqs[i].setStart(newstart);
- command.seqs[i].setEnd(newend);
+ sequence.setSequence(tmp.toString());
+ sequence.setStart(newStart);
+ sequence.setEnd(newEnd);
+
+ /*
+ * command and Undo share the same dataset sequence if cut was
+ * at start or end of sequence
+ */
+ boolean sameDatasetSequence = false;
if (newDSNeeded)
{
- if (command.seqs[i].getDatasetSequence() != null)
+ if (sequence.getDatasetSequence() != null)
{
SequenceI ds;
if (newDSWasNeeded)
@@ -682,21 +705,29 @@ public class EditCommand implements CommandI
{
// make a new DS sequence
// use new ds mechanism here
- 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());
+ String ungapped = AlignSeq.extractGaps(Comparison.GapChars,
+ sequence.getSequenceAsString());
+ ds = new Sequence(sequence.getName(), ungapped,
+ sequence.getStart(), sequence.getEnd());
+ ds.setDescription(sequence.getDescription());
}
if (command.oldds == null)
{
command.oldds = new SequenceI[command.seqs.length];
}
- command.oldds[i] = command.seqs[i].getDatasetSequence();
- command.seqs[i].setDatasetSequence(ds);
+ command.oldds[i] = sequence.getDatasetSequence();
+ sameDatasetSequence = ds == sequence.getDatasetSequence();
+ ds.setSequenceFeatures(sequence.getSequenceFeatures());
+ if (!sameDatasetSequence && command.al.getDataset() != null)
+ {
+ // delete 'undone' sequence from alignment dataset
+ command.al.getDataset()
+ .deleteSequence(sequence.getDatasetSequence());
+ }
+ sequence.setDatasetSequence(ds);
}
- adjustFeatures(command, i, start, end, true);
+ undoCutFeatures(command, command.seqs[i], start, length,
+ sameDatasetSequence);
}
}
adjustAnnotations(command, true, seqWasDeleted, views);
@@ -706,7 +737,7 @@ public class EditCommand implements CommandI
static void replace(Edit command)
{
- StringBuffer tmp;
+ StringBuilder tmp;
String oldstring;
int start = command.position;
int end = command.number;
@@ -719,6 +750,7 @@ public class EditCommand implements CommandI
{
boolean newDSWasNeeded = command.oldds != null
&& command.oldds[i] != null;
+ boolean newStartEndWasNeeded = command.oldStartEnd!=null && command.oldStartEnd[i]!=null;
/**
* cut addHistoryItem(new EditCommand("Cut Sequences", EditCommand.CUT,
@@ -731,49 +763,147 @@ public class EditCommand implements CommandI
* EditCommand.PASTE, sequences, 0, alignment.getWidth(), alignment) );
*
*/
+
+ Range beforeEditedPositions = command.seqs[i].findPositions(1, start);
+ Range afterEditedPositions = command.seqs[i]
+ .findPositions(end + 1, command.seqs[i].getLength());
+
oldstring = command.seqs[i].getSequenceAsString();
- tmp = new StringBuffer(oldstring.substring(0, start));
+ tmp = new StringBuilder(oldstring.substring(0, start));
tmp.append(command.string[i]);
- String nogaprep = jalview.analysis.AlignSeq.extractGaps(
- jalview.util.Comparison.GapChars,
+ String nogaprep = AlignSeq.extractGaps(Comparison.GapChars,
new String(command.string[i]));
- int ipos = command.seqs[i].findPosition(start)
- - command.seqs[i].getStart();
- tmp.append(oldstring.substring(end));
+ if (end < oldstring.length())
+ {
+ tmp.append(oldstring.substring(end));
+ }
+ // stash end prior to updating the sequence object so we can save it if
+ // need be.
+ Range oldstartend = new Range(command.seqs[i].getStart(),
+ command.seqs[i].getEnd());
command.seqs[i].setSequence(tmp.toString());
- command.string[i] = oldstring.substring(start, end).toCharArray();
- String nogapold = jalview.analysis.AlignSeq.extractGaps(
- jalview.util.Comparison.GapChars,
+ command.string[i] = oldstring
+ .substring(start, Math.min(end, oldstring.length()))
+ .toCharArray();
+ String nogapold = AlignSeq.extractGaps(Comparison.GapChars,
new String(command.string[i]));
+
if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
{
- if (newDSWasNeeded)
+ // we may already have dataset and limits stashed...
+ if (newDSWasNeeded || newStartEndWasNeeded)
{
+ if (newDSWasNeeded)
+ {
+ // then just switch the dataset sequence
SequenceI oldds = command.seqs[i].getDatasetSequence();
command.seqs[i].setDatasetSequence(command.oldds[i]);
command.oldds[i] = oldds;
+ }
+ if (newStartEndWasNeeded)
+ {
+ Range newStart = command.oldStartEnd[i];
+ command.oldStartEnd[i] = oldstartend;
+ command.seqs[i].setStart(newStart.getBegin());
+ command.seqs[i].setEnd(newStart.getEnd());
+ }
}
- else
+ else
{
- if (command.oldds == null)
+ // decide if we need a new dataset sequence or modify start/end
+ // first edit the original dataset sequence string
+ SequenceI oldds = command.seqs[i].getDatasetSequence();
+ String osp = oldds.getSequenceAsString();
+ int beforeStartOfEdit = -oldds.getStart() + 1
+ + (beforeEditedPositions == null
+ ? ((afterEditedPositions != null)
+ ? afterEditedPositions.getBegin() - 1
+ : oldstartend.getBegin()
+ + nogapold.length())
+ : beforeEditedPositions.getEnd()
+ );
+ int afterEndOfEdit = -oldds.getStart() + 1
+ + ((afterEditedPositions == null)
+ ? oldstartend.getEnd()
+ : afterEditedPositions.getBegin() - 1);
+ String fullseq = osp.substring(0,
+ beforeStartOfEdit)
+ + nogaprep
+ + osp.substring(afterEndOfEdit);
+
+ // and check if new sequence data is different..
+ if (!fullseq.equalsIgnoreCase(osp))
{
- command.oldds = new SequenceI[command.seqs.length];
- }
- command.oldds[i] = command.seqs[i].getDatasetSequence();
- SequenceI newds = new Sequence(
- command.seqs[i].getDatasetSequence());
- String fullseq, osp = newds.getSequenceAsString();
- fullseq = osp.substring(0, ipos) + nogaprep
- + osp.substring(ipos + nogaprep.length());
- newds.setSequence(fullseq.toUpperCase());
- // TODO: JAL-1131 ensure newly created dataset sequence is added to
- // the set of
- // dataset sequences associated with the alignment.
- // TODO: JAL-1131 fix up any annotation associated with new dataset
- // sequence to ensure that original sequence/annotation relationships
- // are preserved.
- command.seqs[i].setDatasetSequence(newds);
+ // old ds and edited ds are different, so
+ // create the new dataset sequence
+ SequenceI newds = new Sequence(oldds);
+ newds.setSequence(fullseq);
+
+ if (command.oldds == null)
+ {
+ command.oldds = new SequenceI[command.seqs.length];
+ }
+ command.oldds[i] = command.seqs[i].getDatasetSequence();
+
+ // And preserve start/end for good-measure
+ if (command.oldStartEnd == null)
+ {
+ command.oldStartEnd = new Range[command.seqs.length];
+ }
+ command.oldStartEnd[i] = oldstartend;
+ // TODO: JAL-1131 ensure newly created dataset sequence is added to
+ // the set of
+ // dataset sequences associated with the alignment.
+ // TODO: JAL-1131 fix up any annotation associated with new dataset
+ // sequence to ensure that original sequence/annotation
+ // relationships
+ // are preserved.
+ command.seqs[i].setDatasetSequence(newds);
+ }
+ else
+ {
+ if (command.oldStartEnd == null)
+ {
+ command.oldStartEnd = new Range[command.seqs.length];
+ }
+ command.oldStartEnd[i] = new Range(command.seqs[i].getStart(),
+ command.seqs[i].getEnd());
+ if (beforeEditedPositions != null
+ && afterEditedPositions == null)
+ {
+ // modification at end
+ command.seqs[i].setEnd(
+ beforeEditedPositions.getEnd() + nogaprep.length()
+ - nogapold.length());
+ }
+ else if (afterEditedPositions != null
+ && beforeEditedPositions == null)
+ {
+ // modification at start
+ command.seqs[i].setStart(
+ afterEditedPositions.getBegin() - nogaprep.length());
+ }
+ else
+ {
+ // edit covered both start and end. Here we can only guess the
+ // new
+ // start/end
+ String nogapalseq = AlignSeq.extractGaps(Comparison.GapChars,
+ command.seqs[i].getSequenceAsString().toUpperCase());
+ int newStart = command.seqs[i].getDatasetSequence()
+ .getSequenceAsString().indexOf(nogapalseq);
+ if (newStart == -1)
+ {
+ throw new Error(
+ "Implementation Error: could not locate start/end "
+ + "in dataset sequence after an edit of the sequence string");
+ }
+ int newEnd = newStart + nogapalseq.length() - 1;
+ command.seqs[i].setStart(newStart);
+ command.seqs[i].setEnd(newEnd);
+ }
+ }
}
}
tmp = null;
@@ -789,7 +919,7 @@ public class EditCommand implements CommandI
if (modifyVisibility && !insert)
{
// only occurs if a sequence was added or deleted.
- command.deletedAnnotationRows = new Hashtable();
+ command.deletedAnnotationRows = new Hashtable<>();
}
if (command.fullAlignmentHeight)
{
@@ -892,8 +1022,7 @@ public class EditCommand implements CommandI
}
// and then duplicate added annotation on every other alignment
// view
- for (int vnum = 0; views != null
- && vnum < views.length; vnum++)
+ for (int vnum = 0; views != null && vnum < views.length; vnum++)
{
if (views[vnum] != command.al)
{
@@ -948,7 +1077,7 @@ public class EditCommand implements CommandI
if (!insert)
{
- command.deletedAnnotations = new Hashtable();
+ command.deletedAnnotations = new Hashtable<>();
}
int aSize;
@@ -1109,98 +1238,97 @@ public class EditCommand implements CommandI
}
}
- final static void adjustFeatures(Edit command, int index, final int i,
- final int j, boolean insert)
+ /**
+ * Restores features to the state before a Cut.
+ *
+ * - re-add any features deleted by the cut
+ * - remove any truncated features created by the cut
+ * - shift right any features to the right of the cut
+ *
+ *
+ * @param command
+ * the Cut command
+ * @param seq
+ * the sequence the Cut applied to
+ * @param start
+ * the start residue position of the cut
+ * @param length
+ * the number of residues cut
+ * @param sameDatasetSequence
+ * true if dataset sequence and frame of reference were left
+ * unchanged by the Cut
+ */
+ final static void undoCutFeatures(Edit command, SequenceI seq,
+ final int start, final int length, boolean sameDatasetSequence)
{
- SequenceI seq = command.seqs[index];
SequenceI sequence = seq.getDatasetSequence();
if (sequence == null)
{
sequence = seq;
}
- if (insert)
+ /*
+ * shift right features that lie to the right of the restored cut (but not
+ * if dataset sequence unchanged - so coordinates were changed by Cut)
+ */
+ if (!sameDatasetSequence)
{
- if (command.editedFeatures != null
- && command.editedFeatures.containsKey(seq))
+ /*
+ * shift right all features right of and not
+ * contiguous with the cut position
+ */
+ seq.getFeatures().shiftFeatures(start + 1, length);
+
+ /*
+ * shift right any features that start at the cut position,
+ * unless they were truncated
+ */
+ List sfs = seq.getFeatures().findFeatures(start,
+ start);
+ for (SequenceFeature sf : sfs)
{
- sequence.setSequenceFeatures(command.editedFeatures.get(seq));
+ if (sf.getBegin() == start)
+ {
+ if (!command.truncatedFeatures.containsKey(seq)
+ || !command.truncatedFeatures.get(seq).contains(sf))
+ {
+ /*
+ * feature was shifted left to cut position (not truncated),
+ * so shift it back right
+ */
+ SequenceFeature shifted = new SequenceFeature(sf, sf.getBegin()
+ + length, sf.getEnd() + length, sf.getFeatureGroup(),
+ sf.getScore());
+ seq.addSequenceFeature(shifted);
+ seq.deleteFeature(sf);
+ }
+ }
}
-
- return;
}
- List sf = sequence.getFeatures()
- .getPositionalFeatures();
-
- if (sf.isEmpty())
- {
- return;
- }
-
- List oldsf = new ArrayList();
-
- int cSize = j - i;
-
- for (SequenceFeature feature : sf)
+ /*
+ * restore any features that were deleted or truncated
+ */
+ if (command.deletedFeatures != null
+ && command.deletedFeatures.containsKey(seq))
{
- SequenceFeature copy = new SequenceFeature(feature);
-
- oldsf.add(copy);
-
- if (feature.getEnd() < i)
- {
- continue;
- }
-
- if (feature.getBegin() > j)
- {
- int newBegin = copy.getBegin() - cSize;
- int newEnd = copy.getEnd() - cSize;
- SequenceFeature newSf = new SequenceFeature(feature, newBegin,
- newEnd, feature.getFeatureGroup(), feature.getScore());
- sequence.deleteFeature(feature);
- sequence.addSequenceFeature(newSf);
- // feature.setBegin(newBegin);
- // feature.setEnd(newEnd);
- continue;
- }
-
- int newBegin = feature.getBegin();
- int newEnd = feature.getEnd();
- if (newBegin >= i)
- {
- newBegin = i;
- // feature.setBegin(i);
- }
-
- if (newEnd < j)
+ for (SequenceFeature deleted : command.deletedFeatures.get(seq))
{
- newEnd = j - 1;
- // feature.setEnd(j - 1);
+ sequence.addSequenceFeature(deleted);
}
- newEnd = newEnd - cSize;
- // feature.setEnd(feature.getEnd() - (cSize));
-
- sequence.deleteFeature(feature);
- if (newEnd >= newBegin)
- {
- sequence.addSequenceFeature(new SequenceFeature(feature, newBegin,
- newEnd, feature.getFeatureGroup(), feature.getScore()));
- }
- // if (feature.getBegin() > feature.getEnd())
- // {
- // sequence.deleteFeature(feature);
- // }
}
- if (command.editedFeatures == null)
+ /*
+ * delete any truncated features
+ */
+ if (command.truncatedFeatures != null
+ && command.truncatedFeatures.containsKey(seq))
{
- command.editedFeatures = new Hashtable>();
+ for (SequenceFeature amended : command.truncatedFeatures.get(seq))
+ {
+ sequence.deleteFeature(amended);
+ }
}
-
- command.editedFeatures.put(seq, oldsf);
-
}
/**
@@ -1233,7 +1361,7 @@ public class EditCommand implements CommandI
*/
public Map priorState(boolean forUndo)
{
- Map result = new HashMap();
+ Map result = new HashMap<>();
if (getEdits() == null)
{
return result;
@@ -1266,7 +1394,7 @@ public class EditCommand implements CommandI
* Work backwards through the edit list, deriving the sequences before each
* was applied. The final result is the sequence set before any edits.
*/
- Iterator editList = new ReverseListIterator(getEdits());
+ Iterator editList = new ReverseListIterator<>(getEdits());
while (editList.hasNext())
{
Edit oldEdit = editList.next();
@@ -1315,29 +1443,45 @@ public class EditCommand implements CommandI
public class Edit
{
- public SequenceI[] oldds;
+ private SequenceI[] oldds;
+
+ /**
+ * start and end of sequence prior to edit
+ */
+ private Range[] oldStartEnd;
+
+ private boolean fullAlignmentHeight = false;
- boolean fullAlignmentHeight = false;
+ private Map deletedAnnotationRows;
- Hashtable deletedAnnotationRows;
+ private Map deletedAnnotations;
- Hashtable deletedAnnotations;
+ /*
+ * features deleted by the cut (re-add on Undo)
+ * (including the original of any shortened features)
+ */
+ private Map> deletedFeatures;
- Hashtable> editedFeatures;
+ /*
+ * shortened features added by the cut (delete on Undo)
+ */
+ private Map> truncatedFeatures;
- AlignmentI al;
+ private AlignmentI al;
- Action command;
+ final private Action command;
char[][] string;
SequenceI[] seqs;
- int[] alIndex;
+ private int[] alIndex;
- int position, number;
+ private int position;
- char gapChar;
+ private int number;
+
+ private char gapChar;
public Edit(Action cmd, SequenceI[] sqs, int pos, int count,
char gap)
@@ -1352,11 +1496,8 @@ public class EditCommand implements CommandI
Edit(Action cmd, SequenceI[] sqs, int pos, int count,
AlignmentI align)
{
- this.gapChar = align.getGapCharacter();
- this.command = cmd;
- this.seqs = sqs;
- this.position = pos;
- this.number = count;
+ this(cmd, sqs, pos, count, align.getGapCharacter());
+
this.al = align;
alIndex = new int[sqs.length];
@@ -1368,22 +1509,26 @@ public class EditCommand implements CommandI
fullAlignmentHeight = (align.getHeight() == sqs.length);
}
+ /**
+ * Constructor given a REPLACE command and the replacement string
+ *
+ * @param cmd
+ * @param sqs
+ * @param pos
+ * @param count
+ * @param align
+ * @param replace
+ */
Edit(Action cmd, SequenceI[] sqs, int pos, int count,
AlignmentI align, String replace)
{
- this.command = cmd;
- this.seqs = sqs;
- this.position = pos;
- this.number = count;
- this.al = align;
- this.gapChar = align.getGapCharacter();
+ this(cmd, sqs, pos, count, align);
+
string = new char[sqs.length][];
for (int i = 0; i < sqs.length; i++)
{
string[i] = replace.toCharArray();
}
-
- fullAlignmentHeight = (align.getHeight() == sqs.length);
}
public SequenceI[] getSequences()
@@ -1427,7 +1572,163 @@ public class EditCommand implements CommandI
}
else
{
- return new ReverseListIterator(getEdits());
+ return new ReverseListIterator<>(getEdits());
+ }
+ }
+
+ /**
+ * Adjusts features for Cut, and saves details of changes made to allow Undo
+ *
+ * - features left of the cut are unchanged
+ * - features right of the cut are shifted left
+ * - features internal to the cut region are deleted
+ * - features that overlap or span the cut are shortened
+ * - the originals of any deleted or shortened features are saved, to re-add
+ * on Undo
+ * - any added (shortened) features are saved, to delete on Undo
+ *
+ *
+ * @param command
+ * @param seq
+ * @param fromPosition
+ * @param toPosition
+ * @param cutIsInternal
+ */
+ protected static void cutFeatures(Edit command, SequenceI seq,
+ int fromPosition, int toPosition, boolean cutIsInternal)
+ {
+ /*
+ * if the cut is at start or end of sequence
+ * then we don't modify the sequence feature store
+ */
+ if (!cutIsInternal)
+ {
+ return;
+ }
+ List added = new ArrayList<>();
+ List removed = new ArrayList<>();
+
+ SequenceFeaturesI featureStore = seq.getFeatures();
+ if (toPosition < fromPosition || featureStore == null)
+ {
+ return;
+ }
+
+ int cutStartPos = fromPosition;
+ int cutEndPos = toPosition;
+ int cutWidth = cutEndPos - cutStartPos + 1;
+
+ synchronized (featureStore)
+ {
+ /*
+ * get features that overlap the cut region
+ */
+ List toAmend = featureStore.findFeatures(
+ cutStartPos, cutEndPos);
+
+ /*
+ * add any contact features that span the cut region
+ * (not returned by findFeatures)
+ */
+ for (SequenceFeature contact : featureStore.getContactFeatures())
+ {
+ if (contact.getBegin() < cutStartPos
+ && contact.getEnd() > cutEndPos)
+ {
+ toAmend.add(contact);
+ }
+ }
+
+ /*
+ * adjust start-end of overlapping features;
+ * delete features enclosed by the cut;
+ * delete partially overlapping contact features
+ */
+ for (SequenceFeature sf : toAmend)
+ {
+ int sfBegin = sf.getBegin();
+ int sfEnd = sf.getEnd();
+ int newBegin = sfBegin;
+ int newEnd = sfEnd;
+ boolean toDelete = false;
+ boolean follows = false;
+
+ if (sfBegin >= cutStartPos && sfEnd <= cutEndPos)
+ {
+ /*
+ * feature lies within cut region - delete it
+ */
+ toDelete = true;
+ }
+ else if (sfBegin < cutStartPos && sfEnd > cutEndPos)
+ {
+ /*
+ * feature spans cut region - left-shift the end
+ */
+ newEnd -= cutWidth;
+ }
+ else if (sfEnd <= cutEndPos)
+ {
+ /*
+ * feature overlaps left of cut region - truncate right
+ */
+ newEnd = cutStartPos - 1;
+ if (sf.isContactFeature())
+ {
+ toDelete = true;
+ }
+ }
+ else if (sfBegin >= cutStartPos)
+ {
+ /*
+ * remaining case - feature overlaps right
+ * truncate left, adjust end of feature
+ */
+ newBegin = cutIsInternal ? cutStartPos : cutEndPos + 1;
+ newEnd = newBegin + sfEnd - cutEndPos - 1;
+ if (sf.isContactFeature())
+ {
+ toDelete = true;
+ }
+ }
+
+ seq.deleteFeature(sf);
+ if (!follows)
+ {
+ removed.add(sf);
+ }
+ if (!toDelete)
+ {
+ SequenceFeature copy = new SequenceFeature(sf, newBegin, newEnd,
+ sf.getFeatureGroup(), sf.getScore());
+ seq.addSequenceFeature(copy);
+ if (!follows)
+ {
+ added.add(copy);
+ }
+ }
+ }
+
+ /*
+ * and left shift any features lying to the right of the cut region
+ */
+
+ featureStore.shiftFeatures(cutEndPos + 1, -cutWidth);
+ }
+
+ /*
+ * save deleted and amended features, so that Undo can
+ * re-add or delete them respectively
+ */
+ if (command.deletedFeatures == null)
+ {
+ command.deletedFeatures = new HashMap<>();
+ }
+ if (command.truncatedFeatures == null)
+ {
+ command.truncatedFeatures = new HashMap<>();
}
+ command.deletedFeatures.put(seq, removed);
+ command.truncatedFeatures.put(seq, added);
}
}
diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java
index 33de452..f555855 100755
--- a/src/jalview/datamodel/Sequence.java
+++ b/src/jalview/datamodel/Sequence.java
@@ -43,10 +43,7 @@ import fr.orsay.lri.varna.models.rna.RNA;
/**
*
- * Implements the SequenceI interface for a char[] based sequence object.
- *
- * @author $author$
- * @version $Revision$
+ * Implements the SequenceI interface for a char[] based sequence object
*/
public class Sequence extends ASequence implements SequenceI
{
@@ -797,6 +794,7 @@ public class Sequence extends ASequence implements SequenceI
* preserve end residue column provided cursor was valid
*/
int endColumn = isValidCursor(cursor) ? cursor.lastColumnPosition : 0;
+
if (residuePos == this.end)
{
endColumn = column;
@@ -833,8 +831,7 @@ public class Sequence extends ASequence implements SequenceI
/*
* move left or right to find pos from hint.position
*/
- int col = curs.columnPosition - 1; // convert from base 1 to 0-based array
- // index
+ int col = curs.columnPosition - 1; // convert from base 1 to base 0
int newPos = curs.residuePosition;
int delta = newPos > pos ? -1 : 1;
@@ -1263,12 +1260,13 @@ public class Sequence extends ASequence implements SequenceI
boolean createNewDs = false;
// TODO: take a (second look) at the dataset creation validation method for
// the very large sequence case
+
int startIndex = findIndex(start) - 1;
int endIndex = findIndex(end) - 1;
int startDeleteColumn = -1; // for dataset sequence deletions
int deleteCount = 0;
- for (int s = i; s < j; s++)
+ for (int s = i; s < j && s < sequence.length; s++)
{
if (Comparison.isGap(sequence[s]))
{
@@ -1913,6 +1911,15 @@ public class Sequence extends ASequence implements SequenceI
List result = getFeatures().findFeatures(startPos,
endPos, types);
+ if (datasetSequence != null)
+ {
+ result = datasetSequence.getFeatures().findFeatures(startPos, endPos,
+ types);
+ }
+ else
+ {
+ result = sequenceFeatureStore.findFeatures(startPos, endPos, types);
+ }
/*
* if end column is gapped, endPos may be to the right,
diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java
index 944f263..fc8ac49 100755
--- a/src/jalview/datamodel/SequenceGroup.java
+++ b/src/jalview/datamodel/SequenceGroup.java
@@ -102,11 +102,15 @@ public class SequenceGroup implements AnnotatedCollectionI
*/
public ResidueShaderI cs;
- // start column (base 0)
- int startRes = 0;
+ /**
+ * start column (base 0)
+ */
+ private int startRes = 0;
- // end column (base 0)
- int endRes = 0;
+ /**
+ * end column (base 0)
+ */
+ private int endRes = 0;
public Color outlineColour = Color.black;
@@ -209,6 +213,7 @@ public class SequenceGroup implements AnnotatedCollectionI
displayBoxes = seqsel.displayBoxes;
displayText = seqsel.displayText;
colourText = seqsel.colourText;
+
startRes = seqsel.startRes;
endRes = seqsel.endRes;
cs = new ResidueShader((ResidueShader) seqsel.cs);
@@ -752,13 +757,16 @@ public class SequenceGroup implements AnnotatedCollectionI
/**
* Set the first column selected by this group. Runs from 0<=i 0)
+ if (sf.getBegin() >= fromPosition)
{
- newBegin = Math.max(1, newBegin);
- SequenceFeature sf2 = new SequenceFeature(sf, newBegin, newEnd,
- sf.getFeatureGroup(), sf.getScore());
- addFeature(sf2);
+ modified = true;
+ int newBegin = sf.getBegin() + shiftBy;
+ int newEnd = sf.getEnd() + shiftBy;
+
+ /*
+ * sanity check: don't shift left of the first residue
+ */
+ if (newEnd > 0)
+ {
+ newBegin = Math.max(1, newBegin);
+ SequenceFeature sf2 = new SequenceFeature(sf, newBegin, newEnd,
+ sf.getFeatureGroup(), sf.getScore());
+ addFeature(sf2);
+ }
+ delete(sf);
}
- delete(sf);
}
return modified;
}
diff --git a/src/jalview/datamodel/features/SequenceFeatures.java b/src/jalview/datamodel/features/SequenceFeatures.java
index 727d3ef..8f965b4 100644
--- a/src/jalview/datamodel/features/SequenceFeatures.java
+++ b/src/jalview/datamodel/features/SequenceFeatures.java
@@ -472,13 +472,22 @@ public class SequenceFeatures implements SequenceFeaturesI
* {@inheritDoc}
*/
@Override
- public boolean shiftFeatures(int shift)
+ public boolean shiftFeatures(int fromPosition, int shiftBy)
{
boolean modified = false;
for (FeatureStore fs : featureStore.values())
{
- modified |= fs.shiftFeatures(shift);
+ modified |= fs.shiftFeatures(fromPosition, shiftBy);
}
return modified;
}
-}
\ No newline at end of file
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void deleteAll()
+ {
+ featureStore.clear();
+ }
+}
diff --git a/src/jalview/datamodel/features/SequenceFeaturesI.java b/src/jalview/datamodel/features/SequenceFeaturesI.java
index 31712b9..ca0283e 100644
--- a/src/jalview/datamodel/features/SequenceFeaturesI.java
+++ b/src/jalview/datamodel/features/SequenceFeaturesI.java
@@ -215,10 +215,17 @@ public interface SequenceFeaturesI
float getMaximumScore(String type, boolean positional);
/**
- * Adds the shift amount to the start and end of all positional features,
- * returning true if at least one feature was shifted, else false
+ * Adds the shift amount to the start and end of all positional features whose
+ * start position is at or after fromPosition. Returns true if at least one
+ * feature was shifted, else false.
*
- * @param shift
+ * @param fromPosition
+ * @param shiftBy
*/
- abstract boolean shiftFeatures(int shift);
-}
\ No newline at end of file
+ boolean shiftFeatures(int fromPosition, int shiftBy);
+
+ /**
+ * Deletes all positional and non-positional features
+ */
+ void deleteAll();
+}
diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java
index 8832278..a5b1110 100644
--- a/src/jalview/ext/jmol/JalviewJmolBinding.java
+++ b/src/jalview/ext/jmol/JalviewJmolBinding.java
@@ -1254,7 +1254,10 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
return chainNames;
}
- protected abstract IProgressIndicator getIProgressIndicator();
+ protected IProgressIndicator getIProgressIndicator()
+ {
+ return null;
+ }
public void notifyNewPickingModeMeasurement(int iatom, String strMeasure)
{
diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java
index 90d7c35..dbe3317 100644
--- a/src/jalview/gui/FeatureSettings.java
+++ b/src/jalview/gui/FeatureSettings.java
@@ -79,7 +79,6 @@ import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
-import javax.swing.JCheckBoxMenuItem;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
@@ -219,7 +218,8 @@ public class FeatureSettings extends JPanel
FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
column);
tip = o.isEmpty()
- ? MessageManager.getString("label.filters_tooltip")
+ ? MessageManager
+ .getString("label.configure_feature_tooltip")
: o.toString();
break;
default:
@@ -415,69 +415,6 @@ public class FeatureSettings extends JPanel
});
men.add(dens);
- /*
- * variable colour options include colour by label, by score,
- * by selected attribute text, or attribute value
- */
- final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
- MessageManager.getString("label.variable_colour"));
- mxcol.setSelected(!featureColour.isSimpleColour());
- men.add(mxcol);
- mxcol.addActionListener(new ActionListener()
- {
- JColorChooser colorChooser;
-
- @Override
- public void actionPerformed(ActionEvent e)
- {
- if (e.getSource() == mxcol)
- {
- if (featureColour.isSimpleColour())
- {
- FeatureTypeSettings fc = new FeatureTypeSettings(me.fr, type);
- fc.addActionListener(this);
- }
- else
- {
- // bring up simple color chooser
- colorChooser = new JColorChooser();
- String title = MessageManager
- .getString("label.select_colour");
- JDialog dialog = JColorChooser.createDialog(me,
- title, true, // modal
- colorChooser, this, // OK button handler
- null); // no CANCEL button handler
- colorChooser.setColor(featureColour.getMaxColour());
- dialog.setVisible(true);
- }
- }
- else
- {
- if (e.getSource() instanceof FeatureTypeSettings)
- {
- /*
- * update after OK in feature colour dialog; the updated
- * colour will have already been set in the FeatureRenderer
- */
- FeatureColourI fci = fr.getFeatureColours().get(type);
- table.setValueAt(fci, rowSelected, 1);
- table.validate();
- }
- else
- {
- // probably the color chooser!
- table.setValueAt(new FeatureColour(colorChooser.getColor()),
- rowSelected, 1);
- table.validate();
- me.updateFeatureRenderer(
- ((FeatureTableModel) table.getModel()).getData(),
- false);
- }
- }
- }
-
- });
-
JMenuItem selCols = new JMenuItem(
MessageManager.getString("label.select_columns_containing"));
selCols.addActionListener(new ActionListener()
@@ -1354,7 +1291,7 @@ public class FeatureSettings extends JPanel
private String[] columnNames = {
MessageManager.getString("label.feature_type"),
MessageManager.getString("action.colour"),
- MessageManager.getString("label.filter"),
+ MessageManager.getString("label.configuration"),
MessageManager.getString("label.show") };
private Object[][] data;
@@ -1690,14 +1627,17 @@ public class FeatureSettings extends JPanel
{
// bring up graduated chooser.
chooser = new FeatureTypeSettings(me.fr, type);
- chooser.setRequestFocusEnabled(true);
- chooser.requestFocus();
+ /**
+ * @j2sNative
+ */
+ {
+ chooser.setRequestFocusEnabled(true);
+ chooser.requestFocus();
+ }
chooser.addActionListener(this);
- chooser.showTab(true);
+ // Make the renderer reappear.
+ fireEditingStopped();
}
- // Make the renderer reappear.
- fireEditingStopped();
-
}
else
{
@@ -1819,7 +1759,6 @@ public class FeatureSettings extends JPanel
chooser.getWidth(), chooser.getHeight());
chooser.validate();
}
- chooser.showTab(false);
fireEditingStopped();
}
else if (e.getSource() instanceof Component)
diff --git a/src/jalview/gui/FeatureTypeSettings.java b/src/jalview/gui/FeatureTypeSettings.java
index e13f6ee..58bbbe8 100644
--- a/src/jalview/gui/FeatureTypeSettings.java
+++ b/src/jalview/gui/FeatureTypeSettings.java
@@ -62,7 +62,6 @@ import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
-import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.LineBorder;
@@ -107,7 +106,8 @@ public class FeatureTypeSettings extends JalviewDialog
/*
* FeatureRenderer holds colour scheme and filters for feature types
*/
- private final FeatureRenderer fr; // todo refactor to allow interface type here
+ private final FeatureRenderer fr; // todo refactor to allow interface type
+ // here
/*
* the view panel to update when settings change
@@ -155,7 +155,12 @@ public class FeatureTypeSettings extends JalviewDialog
private JRadioButton graduatedColour = new JRadioButton();
- private JPanel singleColour = new JPanel();
+ /**
+ * colours and filters are shown in tabbed view or single content pane
+ */
+ JPanel coloursPanel, filtersPanel;
+
+ JPanel singleColour = new JPanel();
private JPanel minColour = new JPanel();
@@ -199,13 +204,8 @@ public class FeatureTypeSettings extends JalviewDialog
*/
private List filters;
- // set white normally, black to debug layout
- private Color debugBorderColour = Color.white;
-
private JPanel chooseFiltersPanel;
- private JTabbedPane tabbedPane;
-
/**
* Constructor
*
@@ -214,28 +214,14 @@ public class FeatureTypeSettings extends JalviewDialog
*/
public FeatureTypeSettings(FeatureRenderer frender, String theType)
{
- this(frender, false, theType);
- }
-
- /**
- * Constructor, with option to make a blocking dialog (has to complete in the
- * AWT event queue thread). Currently this option is always set to false.
- *
- * @param frender
- * @param blocking
- * @param theType
- */
- FeatureTypeSettings(FeatureRenderer frender, boolean blocking,
- String theType)
- {
this.fr = frender;
this.featureType = theType;
ap = fr.ap;
originalFilter = fr.getFeatureFilter(theType);
originalColour = fr.getFeatureColours().get(theType);
-
+
adjusting = true;
-
+
try
{
initialise();
@@ -244,20 +230,19 @@ public class FeatureTypeSettings extends JalviewDialog
ex.printStackTrace();
return;
}
-
+
updateColoursTab();
-
+
updateFiltersTab();
-
+
adjusting = false;
-
+
colourChanged(false);
-
+
String title = MessageManager
.formatMessage("label.display_settings_for", new String[]
{ theType });
- initDialogFrame(this, true, blocking, title, 600, 360);
-
+ initDialogFrame(this, true, false, title, 580, 500);
waitForInput();
}
@@ -280,9 +265,9 @@ public class FeatureTypeSettings extends JalviewDialog
*/
if (fc.isSimpleColour())
{
- simpleColour.setSelected(true);
singleColour.setBackground(fc.getColour());
singleColour.setForeground(fc.getColour());
+ simpleColour.setSelected(true);
}
/*
@@ -380,7 +365,7 @@ public class FeatureTypeSettings extends JalviewDialog
: BELOW_THRESHOLD_OPTION);
slider.setEnabled(true);
slider.setValue((int) (fc.getThreshold() * scaleFactor));
- thresholdValue.setText(String.valueOf(getRoundedSliderValue()));
+ thresholdValue.setText(String.valueOf(fc.getThreshold()));
thresholdValue.setEnabled(true);
thresholdIsMin.setEnabled(true);
}
@@ -403,8 +388,6 @@ public class FeatureTypeSettings extends JalviewDialog
private void initialise()
{
this.setLayout(new BorderLayout());
- tabbedPane = new JTabbedPane();
- this.add(tabbedPane, BorderLayout.CENTER);
/*
* an ActionListener that applies colour changes
@@ -419,18 +402,16 @@ public class FeatureTypeSettings extends JalviewDialog
};
/*
- * first tab: colour options
+ * first panel/tab: colour options
*/
JPanel coloursPanel = initialiseColoursPanel();
- tabbedPane.addTab(MessageManager.getString("action.colour"),
- coloursPanel);
+ this.add(coloursPanel, BorderLayout.NORTH);
/*
- * second tab: filter options
+ * second panel/tab: filter options
*/
JPanel filtersPanel = initialiseFiltersPanel();
- tabbedPane.addTab(MessageManager.getString("label.filters"),
- filtersPanel);
+ this.add(filtersPanel, BorderLayout.CENTER);
JPanel okCancelPanel = initialiseOkCancelPanel();
@@ -581,12 +562,11 @@ public class FeatureTypeSettings extends JalviewDialog
maxColour.setBorder(new LineBorder(Color.black));
/*
- * default max colour to current colour (if a plain colour),
- * or to Black if colour by label; make min colour a pale
- * version of max colour
+ * default max colour to last plain colour;
+ * make min colour a pale version of max colour
*/
FeatureColourI fc = fr.getFeatureColours().get(featureType);
- Color bg = fc.isSimpleColour() ? fc.getColour() : Color.BLACK;
+ Color bg = fc.getColour() == null ? Color.BLACK : fc.getColour();
maxColour.setBackground(bg);
minColour.setBackground(ColorUtils.bleachColour(bg, 0.9f));
@@ -671,6 +651,7 @@ public class FeatureTypeSettings extends JalviewDialog
{
thresholdValue
.setText(String.valueOf(slider.getValue() / scaleFactor));
+ thresholdValue.setBackground(Color.white); // to reset red for invalid
sliderValueChanged();
}
}
@@ -736,15 +717,16 @@ public class FeatureTypeSettings extends JalviewDialog
private JPanel initialiseColoursPanel()
{
JPanel colourByPanel = new JPanel();
+ colourByPanel.setBackground(Color.white);
colourByPanel.setLayout(new BoxLayout(colourByPanel, BoxLayout.Y_AXIS));
+ JvSwingUtils.createTitledBorder(colourByPanel,
+ MessageManager.getString("action.colour"), true);
/*
* simple colour radio button and colour picker
*/
JPanel simpleColourPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
simpleColourPanel.setBackground(Color.white);
- JvSwingUtils.createTitledBorder(simpleColourPanel,
- MessageManager.getString("label.simple"), true);
colourByPanel.add(simpleColourPanel);
simpleColour = new JRadioButton(
@@ -757,15 +739,24 @@ public class FeatureTypeSettings extends JalviewDialog
{
if (simpleColour.isSelected() && !adjusting)
{
- showColourChooser(singleColour, "label.select_colour");
+ colourChanged(true);
}
}
-
});
-
+
singleColour.setFont(JvSwingUtils.getLabelFont());
singleColour.setBorder(BorderFactory.createLineBorder(Color.black));
singleColour.setPreferredSize(new Dimension(40, 20));
+ if (originalColour.isGraduatedColour())
+ {
+ singleColour.setBackground(originalColour.getMaxColour());
+ singleColour.setForeground(originalColour.getMaxColour());
+ }
+ else
+ {
+ singleColour.setBackground(originalColour.getColour());
+ singleColour.setForeground(originalColour.getColour());
+ }
singleColour.addMouseListener(new MouseAdapter()
{
@Override
@@ -849,9 +840,9 @@ public class FeatureTypeSettings extends JalviewDialog
}
/**
- * Constructs and sets the selected colour options as the colour for the feature
- * type, and repaints the alignment, and optionally the Overview and/or
- * structure viewer if open
+ * Constructs and sets the selected colour options as the colour for the
+ * feature type, and repaints the alignment, and optionally the Overview
+ * and/or structure viewer if open
*
* @param updateStructsAndOverview
*/
@@ -902,8 +893,8 @@ public class FeatureTypeSettings extends JalviewDialog
*/
if (byCategory.isSelected())
{
- Color c = this.getBackground();
- FeatureColourI fc = new FeatureColour(c, c, null, 0f, 0f);
+ Color c = singleColour.getBackground();
+ FeatureColourI fc = new FeatureColour(c);
fc.setColourByLabel(true);
String byWhat = (String) colourByTextCombo.getSelectedItem();
if (!LABEL_18N.equals(byWhat))
@@ -1028,21 +1019,23 @@ public class FeatureTypeSettings extends JalviewDialog
{
try
{
+ /*
+ * set 'adjusting' flag while moving the slider, so it
+ * doesn't then in turn change the value (with rounding)
+ */
adjusting = true;
float f = Float.parseFloat(thresholdValue.getText());
+ f = Float.max(f, this.min);
+ f = Float.min(f, this.max);
+ thresholdValue.setText(String.valueOf(f));
slider.setValue((int) (f * scaleFactor));
threshline.value = f;
thresholdValue.setBackground(Color.white); // ok
-
- /*
- * force repaint of any Overview window or structure
- */
- ap.paintAlignment(true, true);
+ adjusting = false;
+ colourChanged(true);
} catch (NumberFormatException ex)
{
thresholdValue.setBackground(Color.red); // not ok
- } finally
- {
adjusting = false;
}
}
@@ -1065,8 +1058,8 @@ public class FeatureTypeSettings extends JalviewDialog
/**
* Converts the slider value to its absolute value by dividing by the
- * scaleFactor. Rounding errors are squashed by forcing min/max of slider range
- * to the actual min/max of feature score range
+ * scaleFactor. Rounding errors are squashed by forcing min/max of slider
+ * range to the actual min/max of feature score range
*
* @return
*/
@@ -1089,11 +1082,11 @@ public class FeatureTypeSettings extends JalviewDialog
}
/**
- * A helper method to build the drop-down choice of attributes for a feature. If
- * 'withRange' is true, then Score, and any attributes with a min-max range, are
- * added. If 'withText' is true, Label and any known attributes are added. This
- * allows 'categorical numerical' attributes e.g. codon position to be coloured
- * by text.
+ * A helper method to build the drop-down choice of attributes for a feature.
+ * If 'withRange' is true, then Score, and any attributes with a min-max
+ * range, are added. If 'withText' is true, Label and any known attributes are
+ * added. This allows 'categorical numerical' attributes e.g. codon position
+ * to be coloured by text.
*
* Where metadata is available with a description for an attribute, that is
* added as a tooltip.
@@ -1188,7 +1181,6 @@ public class FeatureTypeSettings extends JalviewDialog
{
JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
andOrPanel.setBackground(Color.white);
- andOrPanel.setBorder(BorderFactory.createLineBorder(debugBorderColour));
andFilters = new JRadioButton(MessageManager.getString("label.and"));
orFilters = new JRadioButton(MessageManager.getString("label.or"));
ActionListener actionListener = new ActionListener()
@@ -1271,7 +1263,6 @@ public class FeatureTypeSettings extends JalviewDialog
for (FeatureMatcherI filter : filters)
{
JPanel row = addFilter(filter, attNames, filterIndex);
- row.setBorder(BorderFactory.createLineBorder(debugBorderColour));
chooseFiltersPanel.add(row);
filterIndex++;
}
@@ -1283,7 +1274,8 @@ public class FeatureTypeSettings extends JalviewDialog
/**
* A helper method that constructs a row (panel) with one filter condition:
*
- * - a drop-down list of Label, Score and attribute names to choose from
+ * - a drop-down list of Label, Score and attribute names to choose
+ * from
* - a drop-down list of conditions to choose from
* - a text field for input of a match pattern
* - optionally, a 'remove' button
@@ -1481,8 +1473,8 @@ public class FeatureTypeSettings extends JalviewDialog
* @param selectedCondition
* @param patternField
*/
- private void setNumericHints(String attName,
- Condition selectedCondition, JTextField patternField)
+ private void setNumericHints(String attName, Condition selectedCondition,
+ JTextField patternField)
{
patternField.setToolTipText("");
@@ -1516,11 +1508,11 @@ public class FeatureTypeSettings extends JalviewDialog
}
/**
- * Populates the drop-down list of comparison conditions for the given attribute
- * name. The conditions added depend on the datatype of the attribute values.
- * The supplied condition is set as the selected item in the list, provided it
- * is in the list. If the pattern is now invalid (non-numeric pattern for a
- * numeric condition), it is cleared.
+ * Populates the drop-down list of comparison conditions for the given
+ * attribute name. The conditions added depend on the datatype of the
+ * attribute values. The supplied condition is set as the selected item in the
+ * list, provided it is in the list. If the pattern is now invalid
+ * (non-numeric pattern for a numeric condition), it is cleared.
*
* @param attName
* @param cond
@@ -1599,12 +1591,12 @@ public class FeatureTypeSettings extends JalviewDialog
}
/**
- * Answers true unless a numeric condition has been selected with a non-numeric
- * value. Sets the value field to RED with a tooltip if in error.
+ * Answers true unless a numeric condition has been selected with a
+ * non-numeric value. Sets the value field to RED with a tooltip if in error.
*
- * If the pattern is expected but is empty, this method returns false, but does
- * not mark the field as invalid. This supports selecting an attribute for a new
- * condition before a match pattern has been entered.
+ * If the pattern is expected but is empty, this method returns false, but
+ * does not mark the field as invalid. This supports selecting an attribute
+ * for a new condition before a match pattern has been entered.
*
* @param value
* @param condCombo
@@ -1650,14 +1642,15 @@ public class FeatureTypeSettings extends JalviewDialog
/**
* Constructs a filter condition from the given input fields, and replaces the
- * condition at filterIndex with the new one. Does nothing if the pattern field
- * is blank (unless the match condition is one that doesn't require a pattern,
- * e.g. 'Is present'). Answers true if the filter was updated, else false.
+ * condition at filterIndex with the new one. Does nothing if the pattern
+ * field is blank (unless the match condition is one that doesn't require a
+ * pattern, e.g. 'Is present'). Answers true if the filter was updated, else
+ * false.
*
* This method may update the tooltip on the filter value field to show the
- * value range, if a numeric condition is selected. This ensures the tooltip is
- * updated when a numeric valued attribute is chosen on the last 'add a filter'
- * row.
+ * value range, if a numeric condition is selected. This ensures the tooltip
+ * is updated when a numeric valued attribute is chosen on the last 'add a
+ * filter' row.
*
* @param attCombo
* @param condCombo
@@ -1705,17 +1698,6 @@ public class FeatureTypeSettings extends JalviewDialog
}
/**
- * Makes the dialog visible, at the Feature Colour tab or at the Filters tab
- *
- * @param coloursTab
- */
- public void showTab(boolean coloursTab)
- {
- setVisible(true);
- tabbedPane.setSelectedIndex(coloursTab ? 0 : 1);
- }
-
- /**
* Action on any change to feature filtering, namely
*
* - change of selected attribute
@@ -1723,8 +1705,8 @@ public class FeatureTypeSettings extends JalviewDialog
* - change of match pattern
* - removal of a condition
*
- * The inputs are parsed into a combined filter and this is set for the feature
- * type, and the alignment redrawn.
+ * The inputs are parsed into a combined filter and this is set for the
+ * feature type, and the alignment redrawn.
*/
protected void filtersChanged()
{
diff --git a/src/jalview/gui/OverviewCanvas.java b/src/jalview/gui/OverviewCanvas.java
index cc361a5..89088b8d 100644
--- a/src/jalview/gui/OverviewCanvas.java
+++ b/src/jalview/gui/OverviewCanvas.java
@@ -257,6 +257,7 @@ public class OverviewCanvas extends JComponent
public void dispose()
{
dispose = true;
+ od = null;
synchronized (this)
{
restart = true;
diff --git a/src/jalview/gui/ScalePanel.java b/src/jalview/gui/ScalePanel.java
index e6bba02..6abee08 100755
--- a/src/jalview/gui/ScalePanel.java
+++ b/src/jalview/gui/ScalePanel.java
@@ -276,7 +276,9 @@ public class ScalePanel extends JPanel
{
mouseDragging = false;
- int res = (evt.getX() / av.getCharWidth())
+ int xCords = Math.max(0, evt.getX()); // prevent negative X coordinates
+
+ int res = (xCords / av.getCharWidth())
+ av.getRanges().getStartRes();
if (av.hasHiddenColumns())
diff --git a/src/jalview/schemes/FeatureColour.java b/src/jalview/schemes/FeatureColour.java
index 7d14662..51e7645 100644
--- a/src/jalview/schemes/FeatureColour.java
+++ b/src/jalview/schemes/FeatureColour.java
@@ -321,8 +321,8 @@ public class FeatureColour implements FeatureColourI
} catch (Exception e)
{
throw new IllegalArgumentException(
- "Couldn't parse the minimum value for graduated colour ("
- + descriptor + ")");
+ "Couldn't parse the minimum value for graduated colour ('"
+ + minval + "')");
}
try
{
diff --git a/src/jalview/ws/DBRefFetcher.java b/src/jalview/ws/DBRefFetcher.java
index 061e70c..41e4d0d 100644
--- a/src/jalview/ws/DBRefFetcher.java
+++ b/src/jalview/ws/DBRefFetcher.java
@@ -670,7 +670,8 @@ public class DBRefFetcher implements Runnable
int startShift = absStart - sequenceStart + 1;
if (startShift != 0)
{
- modified |= sequence.getFeatures().shiftFeatures(startShift);
+ modified |= sequence.getFeatures().shiftFeatures(1,
+ startShift);
}
}
}
diff --git a/test/jalview/commands/EditCommandTest.java b/test/jalview/commands/EditCommandTest.java
index 155f00e..2160657 100644
--- a/test/jalview/commands/EditCommandTest.java
+++ b/test/jalview/commands/EditCommandTest.java
@@ -21,8 +21,8 @@
package jalview.commands;
import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertSame;
+import static org.testng.AssertJUnit.assertTrue;
import jalview.commands.EditCommand.Action;
import jalview.commands.EditCommand.Edit;
@@ -34,6 +34,8 @@ import jalview.datamodel.SequenceI;
import jalview.datamodel.features.SequenceFeatures;
import jalview.gui.JvOptionPane;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -50,6 +52,22 @@ import org.testng.annotations.Test;
*/
public class EditCommandTest
{
+ private static Comparator BY_DESCRIPTION = new Comparator()
+ {
+
+ @Override
+ public int compare(SequenceFeature o1, SequenceFeature o2)
+ {
+ return o1.getDescription().compareTo(o2.getDescription());
+ }
+ };
+
+ private EditCommand testee;
+
+ private SequenceI[] seqs;
+
+ private Alignment al;
+
/*
* compute n(n+1)/2 e.g.
* func(5) = 5 + 4 + 3 + 2 + 1 = 15
@@ -66,12 +84,6 @@ public class EditCommandTest
JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
}
- private EditCommand testee;
-
- private SequenceI[] seqs;
-
- private Alignment al;
-
@BeforeMethod(alwaysRun = true)
public void setUp()
{
@@ -141,18 +153,23 @@ public class EditCommandTest
}
/**
- * Test a Paste action, where this adds sequences to an alignment.
+ * Test a Paste action, followed by Undo and Redo
*/
@Test(groups = { "Functional" }, enabled = false)
- // TODO fix so it works
- public void testPaste_addToAlignment()
+ public void testPaste_undo_redo()
{
+ // TODO code this test properly, bearing in mind that:
+ // Paste action requires something on the clipboard (Cut/Copy)
+ // - EditCommand.paste doesn't add sequences to the alignment
+ // ... that is done in AlignFrame.paste()
+ // ... unless as a Redo
+ // ...
+
SequenceI[] newSeqs = new SequenceI[2];
newSeqs[0] = new Sequence("newseq0", "ACEFKL");
newSeqs[1] = new Sequence("newseq1", "JWMPDH");
- Edit ec = testee.new Edit(Action.PASTE, newSeqs, 0, al.getWidth(), al);
- EditCommand.paste(ec, new AlignmentI[] { al });
+ new EditCommand("Paste", Action.PASTE, newSeqs, 0, al.getWidth(), al);
assertEquals(6, al.getSequences().size());
assertEquals("1234567890", seqs[3].getSequenceAsString());
assertEquals("ACEFKL", seqs[4].getSequenceAsString());
@@ -262,12 +279,123 @@ public class EditCommandTest
{
// seem to need a dataset sequence on the edited sequence here
seqs[1].createDatasetSequence();
- new EditCommand("", Action.REPLACE, "ZXY", new SequenceI[] { seqs[1] },
+ assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
+ // NB command.number holds end position for a Replace command
+ new EditCommand("", Action.REPLACE, "Z-xY", new SequenceI[] { seqs[1] },
4, 8, al);
assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
+ assertEquals("fghjZ-xYopq", seqs[1].getSequenceAsString());
+ assertEquals("fghjZxYopq",
+ seqs[1].getDatasetSequence().getSequenceAsString());
assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
assertEquals("1234567890", seqs[3].getSequenceAsString());
- seqs[1] = new Sequence("seq1", "fghjZXYnopq");
+ }
+
+ /**
+ * Test the replace command (used to manually edit a sequence)
+ */
+ @Test(groups = { "Functional" })
+ public void testReplace_withGaps()
+ {
+ SequenceI seq = new Sequence("seq", "ABC--DEF");
+ seq.createDatasetSequence();
+ assertEquals("ABCDEF", seq.getDatasetSequence().getSequenceAsString());
+ assertEquals(1, seq.getStart());
+ assertEquals(6, seq.getEnd());
+
+ /*
+ * replace C- with XYZ
+ * NB arg4 = start column of selection for edit (base 0)
+ * arg5 = column after end of selection for edit
+ */
+ EditCommand edit = new EditCommand("", Action.REPLACE, "xyZ",
+ new SequenceI[]
+ { seq }, 2,
+ 4, al);
+ assertEquals("ABxyZ-DEF", seq.getSequenceAsString());
+ assertEquals(1, seq.getStart());
+ assertEquals(8, seq.getEnd());
+ assertEquals("ABxyZDEF",
+ seq.getDatasetSequence().getSequenceAsString());
+ assertEquals(8, seq.getDatasetSequence().getEnd());
+
+ /*
+ * undo the edit
+ */
+ AlignmentI[] views = new AlignmentI[]
+ { new Alignment(new SequenceI[] { seq }) };
+ edit.undoCommand(views);
+
+ assertEquals("ABC--DEF", seq.getSequenceAsString());
+ assertEquals("ABCDEF", seq.getDatasetSequence().getSequenceAsString());
+ assertEquals(1, seq.getStart());
+ assertEquals(6, seq.getEnd());
+ assertEquals(6, seq.getDatasetSequence().getEnd());
+
+ /*
+ * redo the edit
+ */
+ edit.doCommand(views);
+
+ assertEquals("ABxyZ-DEF", seq.getSequenceAsString());
+ assertEquals(1, seq.getStart());
+ assertEquals(8, seq.getEnd());
+ assertEquals("ABxyZDEF",
+ seq.getDatasetSequence().getSequenceAsString());
+ assertEquals(8, seq.getDatasetSequence().getEnd());
+
+ }
+
+ /**
+ * Test replace command when it doesn't cause a sequence edit (see comment in
+ */
+ @Test(groups = { "Functional" })
+ public void testReplaceFirstResiduesWithGaps()
+ {
+ // test replace when gaps are inserted at start. Start/end should change
+ // w.r.t. original edited sequence.
+ SequenceI dsseq = seqs[1].getDatasetSequence();
+ EditCommand edit = new EditCommand("", Action.REPLACE, "----",
+ new SequenceI[]
+ { seqs[1] }, 0, 4, al);
+
+ // trimmed start
+ assertEquals("----klmnopq", seqs[1].getSequenceAsString());
+ // and ds is preserved
+ assertTrue(dsseq == seqs[1].getDatasetSequence());
+ // and it is unchanged
+ assertEquals("fghjklmnopq", dsseq.getSequenceAsString());
+ // and that alignment sequence start has been adjusted
+ assertEquals(5, seqs[1].getStart());
+ assertEquals(11, seqs[1].getEnd());
+
+ AlignmentI[] views = new AlignmentI[] { new Alignment(seqs) };
+ // and undo
+ edit.undoCommand(views);
+
+ // dataset sequence unchanged
+ assertTrue(dsseq == seqs[1].getDatasetSequence());
+ // restore sequence
+ assertEquals("fghjklmnopq", seqs[1].getSequenceAsString());
+ // and start/end numbering also restored
+ assertEquals(1, seqs[1].getStart());
+ assertEquals(11, seqs[1].getEnd());
+
+ // now redo
+ edit.undoCommand(views);
+
+ // and repeat asserts for the original edit
+
+ // trimmed start
+ assertEquals("----klmnopq", seqs[1].getSequenceAsString());
+ // and ds is preserved
+ assertTrue(dsseq == seqs[1].getDatasetSequence());
+ // and it is unchanged
+ assertEquals("fghjklmnopq", dsseq.getSequenceAsString());
+ // and that alignment sequence start has been adjusted
+ assertEquals(5, seqs[1].getStart());
+ assertEquals(11, seqs[1].getEnd());
+
}
/**
@@ -663,7 +791,7 @@ public class EditCommandTest
* create sequence features before, after and overlapping
* a cut of columns/residues 4-7
*/
- SequenceI seq0 = seqs[0];
+ SequenceI seq0 = seqs[0]; // abcdefghjk/1-10
seq0.addSequenceFeature(new SequenceFeature("before", "", 1, 3, 0f,
null));
seq0.addSequenceFeature(new SequenceFeature("overlap left", "", 2, 6,
@@ -675,29 +803,52 @@ public class EditCommandTest
seq0.addSequenceFeature(new SequenceFeature("after", "", 8, 10, 0f,
null));
+ /*
+ * add some contact features
+ */
+ SequenceFeature internalContact = new SequenceFeature("disulphide bond", "", 5,
+ 6, 0f, null);
+ seq0.addSequenceFeature(internalContact); // should get deleted
+ SequenceFeature overlapLeftContact = new SequenceFeature(
+ "disulphide bond", "", 2, 6, 0f, null);
+ seq0.addSequenceFeature(overlapLeftContact); // should get deleted
+ SequenceFeature overlapRightContact = new SequenceFeature(
+ "disulphide bond", "", 5, 8, 0f, null);
+ seq0.addSequenceFeature(overlapRightContact); // should get deleted
+ SequenceFeature spanningContact = new SequenceFeature(
+ "disulphide bond", "", 2, 9, 0f, null);
+ seq0.addSequenceFeature(spanningContact); // should get shortened 3'
+
+ /*
+ * cut columns 3-6 (base 0), residues d-g 4-7
+ */
Edit ec = testee.new Edit(Action.CUT, seqs, 3, 4, al); // cols 3-6 base 0
EditCommand.cut(ec, new AlignmentI[] { al });
List sfs = seq0.getSequenceFeatures();
SequenceFeatures.sortFeatures(sfs, true);
- assertEquals(4, sfs.size()); // feature internal to cut has been deleted
+ assertEquals(5, sfs.size()); // features internal to cut were deleted
SequenceFeature sf = sfs.get(0);
assertEquals("before", sf.getType());
assertEquals(1, sf.getBegin());
assertEquals(3, sf.getEnd());
sf = sfs.get(1);
+ assertEquals("disulphide bond", sf.getType());
+ assertEquals(2, sf.getBegin());
+ assertEquals(5, sf.getEnd()); // truncated by cut
+ sf = sfs.get(2);
assertEquals("overlap left", sf.getType());
assertEquals(2, sf.getBegin());
assertEquals(3, sf.getEnd()); // truncated by cut
- sf = sfs.get(2);
- assertEquals("overlap right", sf.getType());
- assertEquals(4, sf.getBegin()); // shifted left by cut
- assertEquals(5, sf.getEnd()); // truncated by cut
sf = sfs.get(3);
assertEquals("after", sf.getType());
assertEquals(4, sf.getBegin()); // shifted left by cut
assertEquals(6, sf.getEnd()); // shifted left by cut
+ sf = sfs.get(4);
+ assertEquals("overlap right", sf.getType());
+ assertEquals(4, sf.getBegin()); // shifted left by cut
+ assertEquals(4, sf.getEnd()); // truncated by cut
}
/**
@@ -711,16 +862,28 @@ public class EditCommandTest
* create a sequence features on each subrange of 1-5
*/
SequenceI seq0 = new Sequence("seq", "ABCDE");
+ int start = 8;
+ int end = 12;
+ seq0.setStart(start);
+ seq0.setEnd(end);
AlignmentI alignment = new Alignment(new SequenceI[] { seq0 });
alignment.setDataset(null);
- for (int from = 1; from <= seq0.getLength(); from++)
+
+ /*
+ * create a new alignment with shared dataset sequence
+ */
+ AlignmentI copy = new Alignment(
+ new SequenceI[]
+ { alignment.getDataset().getSequenceAt(0).deriveSequence() });
+ SequenceI copySeq0 = copy.getSequenceAt(0);
+
+ for (int from = start; from <= end; from++)
{
- for (int to = from; to <= seq0.getLength(); to++)
+ for (int to = from; to <= end; to++)
{
String desc = String.format("%d-%d", from, to);
SequenceFeature sf = new SequenceFeature("test", desc, from, to,
- 0f,
- null);
+ 0f, null);
sf.setValue("from", Integer.valueOf(from));
sf.setValue("to", Integer.valueOf(to));
seq0.addSequenceFeature(sf);
@@ -729,109 +892,234 @@ public class EditCommandTest
// sanity check
List sfs = seq0.getSequenceFeatures();
assertEquals(func(5), sfs.size());
+ assertEquals(sfs, copySeq0.getSequenceFeatures());
+ String copySequenceFeatures = copySeq0.getSequenceFeatures().toString();
/*
- * now perform all possible cuts of subranges of 1-5 (followed by Undo)
+ * now perform all possible cuts of subranges of columns 1-5
* and validate the resulting remaining sequence features!
*/
SequenceI[] sqs = new SequenceI[] { seq0 };
- // goal is to have this passing for all from/to values!!
- // for (int from = 0; from < seq0.getLength(); from++)
- // {
- // for (int to = from; to < seq0.getLength(); to++)
- for (int from = 1; from < 3; from++)
+ for (int from = 0; from < seq0.getLength(); from++)
{
- for (int to = 2; to < 3; to++)
+ for (int to = from; to < seq0.getLength(); to++)
{
- testee.appendEdit(Action.CUT, sqs, from, (to - from + 1),
- alignment, true);
+ EditCommand ec = new EditCommand("Cut", Action.CUT, sqs, from, (to
+ - from + 1), alignment);
+ final String msg = String.format("Cut %d-%d ", from + 1, to + 1);
+ boolean newDatasetSequence = copySeq0.getDatasetSequence() != seq0
+ .getDatasetSequence();
+
+ verifyCut(seq0, from, to, msg, start);
+
+ /*
+ * verify copy alignment dataset sequence unaffected
+ */
+ assertEquals("Original dataset sequence was modified",
+ copySequenceFeatures,
+ copySeq0.getSequenceFeatures().toString());
+ /*
+ * verify any new dataset sequence was added to the
+ * alignment dataset
+ */
+ assertEquals("Wrong Dataset size after " + msg,
+ newDatasetSequence ? 2 : 1,
+ alignment.getDataset().getHeight());
+
+ /*
+ * undo and verify all restored
+ */
+ AlignmentI[] views = new AlignmentI[] { alignment };
+ ec.undoCommand(views);
sfs = seq0.getSequenceFeatures();
+ assertEquals("After undo of " + msg, func(5), sfs.size());
+ verifyUndo(from, to, sfs);
+
+ /*
+ * verify copy alignment dataset sequence still unaffected
+ * and alignment dataset has shrunk (if it was added to)
+ */
+ assertEquals("Original dataset sequence was modified",
+ copySequenceFeatures,
+ copySeq0.getSequenceFeatures().toString());
+ assertEquals("Wrong Dataset size after Undo of " + msg, 1,
+ alignment.getDataset().getHeight());
/*
- * confirm the number of features has reduced by the
- * number of features within the cut region i.e. by
- * func(length of cut)
+ * redo and verify
*/
- String msg = String.format("Cut %d-%d ", from, to);
- if (to - from == 4)
- {
- // all columns cut
- assertNull(sfs);
- }
- else
- {
- assertEquals(msg + "wrong number of features left", func(5)
- - func(to - from + 1), sfs.size());
- }
+ ec.doCommand(views);
+ verifyCut(seq0, from, to, msg, start);
/*
- * inspect individual features
+ * verify copy alignment dataset sequence unaffected
+ * and any new dataset sequence readded to alignment dataset
*/
- if (sfs != null)
- {
- for (SequenceFeature sf : sfs)
- {
- checkFeatureRelocation(sf, from + 1, to + 1);
- }
- }
+ assertEquals("Original dataset sequence was modified",
+ copySequenceFeatures,
+ copySeq0.getSequenceFeatures().toString());
+ assertEquals("Wrong Dataset size after Redo of " + msg,
+ newDatasetSequence ? 2 : 1,
+ alignment.getDataset().getHeight());
+
/*
* undo ready for next cut
*/
- testee.undoCommand(new AlignmentI[] { alignment });
- assertEquals(func(5), seq0.getSequenceFeatures().size());
+ ec.undoCommand(views);
+
+ /*
+ * final verify that copy alignment dataset sequence is still unaffected
+ * and that alignment dataset has shrunk
+ */
+ assertEquals("Original dataset sequence was modified",
+ copySequenceFeatures,
+ copySeq0.getSequenceFeatures().toString());
+ assertEquals("Wrong Dataset size after final Undo of " + msg, 1,
+ alignment.getDataset().getHeight());
}
}
}
/**
+ * Verify by inspection that the sequence features left on the sequence after
+ * a cut match the expected results. The trick to this is that we can parse
+ * each feature's original start-end positions from its description.
+ *
+ * @param seq0
+ * @param from
+ * @param to
+ * @param msg
+ * @param seqStart
+ */
+ protected void verifyCut(SequenceI seq0, int from, int to,
+ final String msg, int seqStart)
+ {
+ List sfs;
+ sfs = seq0.getSequenceFeatures();
+
+ Collections.sort(sfs, BY_DESCRIPTION);
+
+ /*
+ * confirm the number of features has reduced by the
+ * number of features within the cut region i.e. by
+ * func(length of cut); exception is a cut at start or end of sequence,
+ * which retains the original coordinates, dataset sequence
+ * and all its features
+ */
+ boolean datasetRetained = from == 0 || to == 4;
+ if (datasetRetained)
+ {
+ // dataset and all features retained
+ assertEquals(msg, func(5), sfs.size());
+ }
+ else if (to - from == 4)
+ {
+ // all columns were cut
+ assertTrue(sfs.isEmpty());
+ }
+ else
+ {
+ // failure in checkFeatureRelocation is more informative!
+ assertEquals(msg + "wrong number of features left", func(5)
+ - func(to - from + 1), sfs.size());
+ }
+
+ /*
+ * inspect individual features
+ */
+ for (SequenceFeature sf : sfs)
+ {
+ verifyFeatureRelocation(sf, from + 1, to + 1, !datasetRetained,
+ seqStart);
+ }
+ }
+
+ /**
+ * Check that after Undo, every feature has start/end that match its original
+ * "start" and "end" properties
+ *
+ * @param from
+ * @param to
+ * @param sfs
+ */
+ protected void verifyUndo(int from, int to, List sfs)
+ {
+ for (SequenceFeature sf : sfs)
+ {
+ final int oldFrom = ((Integer) sf.getValue("from")).intValue();
+ final int oldTo = ((Integer) sf.getValue("to")).intValue();
+ String msg = String.format(
+ "Undo cut of [%d-%d], feature at [%d-%d] ", from + 1, to + 1,
+ oldFrom, oldTo);
+ assertEquals(msg + "start", oldFrom, sf.getBegin());
+ assertEquals(msg + "end", oldTo, sf.getEnd());
+ }
+ }
+
+ /**
* Helper method to check a feature has been correctly relocated after a cut
*
* @param sf
* @param from
- * start of cut (first residue cut)
+ * start of cut (first residue cut 1..)
* @param to
- * end of cut (last residue cut)
+ * end of cut (last residue cut 1..)
+ * @param newDataset
+ * @param seqStart
*/
- private void checkFeatureRelocation(SequenceFeature sf, int from, int to)
+ private void verifyFeatureRelocation(SequenceFeature sf, int from, int to,
+ boolean newDataset, int seqStart)
{
// TODO handle the gapped sequence case as well
int cutSize = to - from + 1;
- int oldFrom = ((Integer) sf.getValue("from")).intValue();
- int oldTo = ((Integer) sf.getValue("to")).intValue();
+ final int oldFrom = ((Integer) sf.getValue("from")).intValue();
+ final int oldTo = ((Integer) sf.getValue("to")).intValue();
+ final int oldFromPosition = oldFrom - seqStart + 1; // 1..
+ final int oldToPosition = oldTo - seqStart + 1; // 1..
String msg = String.format(
"Feature %s relocated to %d-%d after cut of %d-%d",
sf.getDescription(), sf.getBegin(), sf.getEnd(), from, to);
- if (oldTo < from)
+ if (!newDataset)
+ {
+ // dataset retained with all features unchanged
+ assertEquals("0: " + msg, oldFrom, sf.getBegin());
+ assertEquals("0: " + msg, oldTo, sf.getEnd());
+ }
+ else if (oldToPosition < from)
{
// before cut region so unchanged
assertEquals("1: " + msg, oldFrom, sf.getBegin());
assertEquals("2: " + msg, oldTo, sf.getEnd());
}
- else if (oldFrom > to)
+ else if (oldFromPosition > to)
{
// follows cut region - shift by size of cut
- assertEquals("3: " + msg, oldFrom - cutSize, sf.getBegin());
- assertEquals("4: " + msg, oldTo - cutSize, sf.getEnd());
+ assertEquals("3: " + msg, newDataset ? oldFrom - cutSize : oldFrom,
+ sf.getBegin());
+ assertEquals("4: " + msg, newDataset ? oldTo - cutSize : oldTo,
+ sf.getEnd());
}
- else if (oldFrom < from && oldTo > to)
+ else if (oldFromPosition < from && oldToPosition > to)
{
// feature encloses cut region - shrink it right
assertEquals("5: " + msg, oldFrom, sf.getBegin());
assertEquals("6: " + msg, oldTo - cutSize, sf.getEnd());
}
- else if (oldFrom < from)
+ else if (oldFromPosition < from)
{
// feature overlaps left side of cut region - truncated right
- assertEquals("7: " + msg, from - 1, sf.getEnd());
+ assertEquals("7: " + msg, from - 1 + seqStart - 1, sf.getEnd());
}
- else if (oldTo > to)
+ else if (oldToPosition > to)
{
// feature overlaps right side of cut region - truncated left
- assertEquals("8: " + msg, from, sf.getBegin());
- assertEquals("9: " + msg, from + oldTo - to - 1, sf.getEnd());
+ assertEquals("8: " + msg, newDataset ? from + seqStart - 1 : to + 1,
+ sf.getBegin());
+ assertEquals("9: " + msg, newDataset ? from + oldTo - to - 1 : oldTo,
+ sf.getEnd());
}
else
{
@@ -844,30 +1132,32 @@ public class EditCommandTest
* Test a cut action's relocation of sequence features
*/
@Test(groups = { "Functional" })
- public void testCut_gappedWithFeatures()
+ public void testCut_withFeatures5prime()
{
+ SequenceI seq0 = new Sequence("seq/8-11", "A-BCC");
+ seq0.createDatasetSequence();
+ assertEquals(8, seq0.getStart());
+ seq0.addSequenceFeature(new SequenceFeature("", "", 10, 11, 0f,
+ null));
+ SequenceI[] seqsArray = new SequenceI[] { seq0 };
+ AlignmentI alignment = new Alignment(seqsArray);
+
/*
- * create sequence features before, after and overlapping
- * a cut of columns/residues 4-7
+ * cut columns of A-B; same dataset sequence is retained, aligned sequence
+ * start becomes 10
*/
- SequenceI seq0 = new Sequence("seq", "A-BCC");
- seq0.addSequenceFeature(new SequenceFeature("", "", 3, 4, 0f,
- null));
- AlignmentI alignment = new Alignment(new SequenceI[] { seq0 });
- // cut columns of A-B
- Edit ec = testee.new Edit(Action.CUT, seqs, 0, 3, alignment); // cols 0-3
- // base 0
+ Edit ec = testee.new Edit(Action.CUT, seqsArray, 0, 3, alignment);
EditCommand.cut(ec, new AlignmentI[] { alignment });
/*
- * feature on CC(3-4) should now be on CC(1-2)
+ * feature on CC(10-11) should still be on CC(10-11)
*/
+ assertSame(seq0, alignment.getSequenceAt(0));
+ assertEquals(10, seq0.getStart());
List sfs = seq0.getSequenceFeatures();
assertEquals(1, sfs.size());
SequenceFeature sf = sfs.get(0);
- assertEquals(1, sf.getBegin());
- assertEquals(2, sf.getEnd());
-
- // TODO add further cases including Undo - see JAL-2541
+ assertEquals(10, sf.getBegin());
+ assertEquals(11, sf.getEnd());
}
}
diff --git a/test/jalview/datamodel/SequenceTest.java b/test/jalview/datamodel/SequenceTest.java
index 79bb2bb..9629b6f 100644
--- a/test/jalview/datamodel/SequenceTest.java
+++ b/test/jalview/datamodel/SequenceTest.java
@@ -290,6 +290,61 @@ public class SequenceTest
assertEquals(0, sq.findIndex(2));
}
+ @Test(groups = { "Functional" })
+ public void testFindPositions()
+ {
+ SequenceI sq = new Sequence("test/8-13", "-ABC---DE-F--");
+
+ /*
+ * invalid inputs
+ */
+ assertNull(sq.findPositions(6, 5));
+ assertNull(sq.findPositions(0, 5));
+ assertNull(sq.findPositions(-1, 5));
+
+ /*
+ * all gapped ranges
+ */
+ assertNull(sq.findPositions(1, 1)); // 1-based columns
+ assertNull(sq.findPositions(5, 5));
+ assertNull(sq.findPositions(5, 6));
+ assertNull(sq.findPositions(5, 7));
+
+ /*
+ * all ungapped ranges
+ */
+ assertEquals(new Range(8, 8), sq.findPositions(2, 2)); // A
+ assertEquals(new Range(8, 9), sq.findPositions(2, 3)); // AB
+ assertEquals(new Range(8, 10), sq.findPositions(2, 4)); // ABC
+ assertEquals(new Range(9, 10), sq.findPositions(3, 4)); // BC
+
+ /*
+ * gap to ungapped range
+ */
+ assertEquals(new Range(8, 10), sq.findPositions(1, 4)); // ABC
+ assertEquals(new Range(11, 12), sq.findPositions(6, 9)); // DE
+
+ /*
+ * ungapped to gapped range
+ */
+ assertEquals(new Range(10, 10), sq.findPositions(4, 5)); // C
+ assertEquals(new Range(9, 13), sq.findPositions(3, 11)); // BCDEF
+
+ /*
+ * ungapped to ungapped enclosing gaps
+ */
+ assertEquals(new Range(10, 11), sq.findPositions(4, 8)); // CD
+ assertEquals(new Range(8, 13), sq.findPositions(2, 11)); // ABCDEF
+
+ /*
+ * gapped to gapped enclosing ungapped
+ */
+ assertEquals(new Range(8, 10), sq.findPositions(1, 5)); // ABC
+ assertEquals(new Range(11, 12), sq.findPositions(5, 10)); // DE
+ assertEquals(new Range(8, 13), sq.findPositions(1, 13)); // the lot
+ assertEquals(new Range(8, 13), sq.findPositions(1, 99));
+ }
+
/**
* Tests for the method that returns a dataset sequence position (start..) for
* an aligned column position (base 0).
@@ -465,8 +520,7 @@ public class SequenceTest
assertEquals("test:Pos13:Col10:startCol3:endCol10:tok0",
PA.getValue(sq, "cursor").toString());
sq.sequenceChanged();
- assertEquals(12, sq.findPosition(8));
- cursor = (SequenceCursor) PA.getValue(sq, "cursor");
+ assertEquals(12, sq.findPosition(8)); // E12
// sequenceChanged() invalidates cursor.lastResidueColumn
cursor = (SequenceCursor) PA.getValue(sq, "cursor");
assertEquals("test:Pos12:Col9:startCol3:endCol0:tok1",
@@ -505,6 +559,13 @@ public class SequenceTest
assertEquals(6, sq.getEnd());
assertNull(PA.getValue(sq, "datasetSequence"));
+ sq = new Sequence("test", "ABCDE");
+ sq.deleteChars(0, 3);
+ assertEquals("DE", sq.getSequenceAsString());
+ assertEquals(4, sq.getStart());
+ assertEquals(5, sq.getEnd());
+ assertNull(PA.getValue(sq, "datasetSequence"));
+
/*
* delete at end
*/
@@ -514,6 +575,21 @@ public class SequenceTest
assertEquals(1, sq.getStart());
assertEquals(4, sq.getEnd());
assertNull(PA.getValue(sq, "datasetSequence"));
+
+ /*
+ * delete more positions than there are
+ */
+ sq = new Sequence("test/8-11", "ABCD");
+ sq.deleteChars(0, 99);
+ assertEquals("", sq.getSequenceAsString());
+ assertEquals(12, sq.getStart()); // = findPosition(99) ?!?
+ assertEquals(11, sq.getEnd());
+
+ sq = new Sequence("test/8-11", "----");
+ sq.deleteChars(0, 99); // ArrayIndexOutOfBoundsException <= 2.10.2
+ assertEquals("", sq.getSequenceAsString());
+ assertEquals(8, sq.getStart());
+ assertEquals(11, sq.getEnd());
}
@Test(groups = { "Functional" })
@@ -1614,6 +1690,10 @@ public class SequenceTest
// cursor should now be at [D 6]
cursor = (SequenceCursor) PA.getValue(sq, "cursor");
assertEquals(new SequenceCursor(sq, 11, 6, ++token), cursor);
+ assertEquals(0, cursor.lastColumnPosition); // not yet found
+ assertEquals(13, sq.findPosition(8)); // E13
+ cursor = (SequenceCursor) PA.getValue(sq, "cursor");
+ assertEquals(9, cursor.lastColumnPosition); // found
/*
* deleteChars should invalidate the cached cursor
@@ -1693,61 +1773,6 @@ public class SequenceTest
}
@Test(groups = { "Functional" })
- public void testFindPositions()
- {
- SequenceI sq = new Sequence("test/8-13", "-ABC---DE-F--");
-
- /*
- * invalid inputs
- */
- assertNull(sq.findPositions(6, 5));
- assertNull(sq.findPositions(0, 5));
- assertNull(sq.findPositions(-1, 5));
-
- /*
- * all gapped ranges
- */
- assertNull(sq.findPositions(1, 1)); // 1-based columns
- assertNull(sq.findPositions(5, 5));
- assertNull(sq.findPositions(5, 6));
- assertNull(sq.findPositions(5, 7));
-
- /*
- * all ungapped ranges
- */
- assertEquals(new Range(8, 8), sq.findPositions(2, 2)); // A
- assertEquals(new Range(8, 9), sq.findPositions(2, 3)); // AB
- assertEquals(new Range(8, 10), sq.findPositions(2, 4)); // ABC
- assertEquals(new Range(9, 10), sq.findPositions(3, 4)); // BC
-
- /*
- * gap to ungapped range
- */
- assertEquals(new Range(8, 10), sq.findPositions(1, 4)); // ABC
- assertEquals(new Range(11, 12), sq.findPositions(6, 9)); // DE
-
- /*
- * ungapped to gapped range
- */
- assertEquals(new Range(10, 10), sq.findPositions(4, 5)); // C
- assertEquals(new Range(9, 13), sq.findPositions(3, 11)); // BCDEF
-
- /*
- * ungapped to ungapped enclosing gaps
- */
- assertEquals(new Range(10, 11), sq.findPositions(4, 8)); // CD
- assertEquals(new Range(8, 13), sq.findPositions(2, 11)); // ABCDEF
-
- /*
- * gapped to gapped enclosing ungapped
- */
- assertEquals(new Range(8, 10), sq.findPositions(1, 5)); // ABC
- assertEquals(new Range(11, 12), sq.findPositions(5, 10)); // DE
- assertEquals(new Range(8, 13), sq.findPositions(1, 13)); // the lot
- assertEquals(new Range(8, 13), sq.findPositions(1, 99));
- }
-
- @Test(groups = { "Functional" })
public void testGapBitset()
{
SequenceI sq = new Sequence("test/8-13", "-ABC---DE-F--");
diff --git a/test/jalview/datamodel/features/FeatureStoreTest.java b/test/jalview/datamodel/features/FeatureStoreTest.java
index db21c2f..6e7dd02 100644
--- a/test/jalview/datamodel/features/FeatureStoreTest.java
+++ b/test/jalview/datamodel/features/FeatureStoreTest.java
@@ -2,6 +2,7 @@ package jalview.datamodel.features;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue;
import jalview.datamodel.SequenceFeature;
@@ -774,7 +775,7 @@ public class FeatureStoreTest
public void testShiftFeatures()
{
FeatureStore fs = new FeatureStore();
- assertFalse(fs.shiftFeatures(1));
+ assertFalse(fs.shiftFeatures(0, 1)); // nothing to do
SequenceFeature sf1 = new SequenceFeature("Cath", "", 2, 5, 0f, null);
fs.addFeature(sf1);
@@ -790,9 +791,9 @@ public class FeatureStoreTest
fs.addFeature(sf4);
/*
- * shift features right by 5
+ * shift all features right by 5
*/
- assertTrue(fs.shiftFeatures(5));
+ assertTrue(fs.shiftFeatures(0, 5));
// non-positional features untouched:
List nonPos = fs.getNonPositionalFeatures();
@@ -818,7 +819,7 @@ public class FeatureStoreTest
* feature at [7-10] should be removed
* feature at [13-19] should become [1-4]
*/
- assertTrue(fs.shiftFeatures(-15));
+ assertTrue(fs.shiftFeatures(0, -15));
pos = fs.getPositionalFeatures();
assertEquals(pos.size(), 2);
SequenceFeatures.sortFeatures(pos, true);
@@ -826,6 +827,32 @@ public class FeatureStoreTest
assertEquals(pos.get(0).getEnd(), 4);
assertEquals(pos.get(1).getBegin(), 13);
assertEquals(pos.get(1).getEnd(), 22);
+
+ /*
+ * shift right by 4 from position 2 onwards
+ * feature at [1-4] unchanged, feature at [13-22] shifts
+ */
+ assertTrue(fs.shiftFeatures(2, 4));
+ pos = fs.getPositionalFeatures();
+ assertEquals(pos.size(), 2);
+ SequenceFeatures.sortFeatures(pos, true);
+ assertEquals(pos.get(0).getBegin(), 1);
+ assertEquals(pos.get(0).getEnd(), 4);
+ assertEquals(pos.get(1).getBegin(), 17);
+ assertEquals(pos.get(1).getEnd(), 26);
+
+ /*
+ * shift right by 4 from position 18 onwards
+ * should be no change
+ */
+ SequenceFeature f1 = pos.get(0);
+ SequenceFeature f2 = pos.get(1);
+ assertFalse(fs.shiftFeatures(18, 4)); // no update
+ pos = fs.getPositionalFeatures();
+ assertEquals(pos.size(), 2);
+ SequenceFeatures.sortFeatures(pos, true);
+ assertSame(pos.get(0), f1);
+ assertSame(pos.get(1), f2);
}
@Test(groups = "Functional")
diff --git a/test/jalview/datamodel/features/SequenceFeaturesTest.java b/test/jalview/datamodel/features/SequenceFeaturesTest.java
index 32987b0..29e76bb 100644
--- a/test/jalview/datamodel/features/SequenceFeaturesTest.java
+++ b/test/jalview/datamodel/features/SequenceFeaturesTest.java
@@ -1161,7 +1161,7 @@ public class SequenceFeaturesTest
public void testShiftFeatures()
{
SequenceFeatures store = new SequenceFeatures();
- assertFalse(store.shiftFeatures(1));
+ assertFalse(store.shiftFeatures(0, 1));
SequenceFeature sf1 = new SequenceFeature("Cath", "", 2, 5, 0f, null);
store.add(sf1);
@@ -1179,7 +1179,7 @@ public class SequenceFeaturesTest
/*
* shift features right by 5
*/
- assertTrue(store.shiftFeatures(5));
+ assertTrue(store.shiftFeatures(0, 5));
// non-positional features untouched:
List nonPos = store.getNonPositionalFeatures();
@@ -1208,7 +1208,7 @@ public class SequenceFeaturesTest
* feature at [7-10] should be removed
* feature at [13-19] should become [1-4]
*/
- assertTrue(store.shiftFeatures(-15));
+ assertTrue(store.shiftFeatures(0, -15));
pos = store.getPositionalFeatures();
assertEquals(pos.size(), 2);
SequenceFeatures.sortFeatures(pos, true);
@@ -1218,6 +1218,35 @@ public class SequenceFeaturesTest
assertEquals(pos.get(1).getBegin(), 13);
assertEquals(pos.get(1).getEnd(), 22);
assertEquals(pos.get(1).getType(), "Disulfide bond");
+
+ /*
+ * shift right by 4 from column 2
+ * feature at [1-4] should be unchanged
+ * feature at [13-22] should become [17-26]
+ */
+ assertTrue(store.shiftFeatures(2, 4));
+ pos = store.getPositionalFeatures();
+ assertEquals(pos.size(), 2);
+ SequenceFeatures.sortFeatures(pos, true);
+ assertEquals(pos.get(0).getBegin(), 1);
+ assertEquals(pos.get(0).getEnd(), 4);
+ assertEquals(pos.get(0).getType(), "Metal");
+ assertEquals(pos.get(1).getBegin(), 17);
+ assertEquals(pos.get(1).getEnd(), 26);
+ assertEquals(pos.get(1).getType(), "Disulfide bond");
+
+ /*
+ * shift right from column 18
+ * should be no updates
+ */
+ SequenceFeature f1 = pos.get(0);
+ SequenceFeature f2 = pos.get(1);
+ assertFalse(store.shiftFeatures(18, 6));
+ pos = store.getPositionalFeatures();
+ assertEquals(pos.size(), 2);
+ SequenceFeatures.sortFeatures(pos, true);
+ assertSame(pos.get(0), f1);
+ assertSame(pos.get(1), f2);
}
@Test(groups = "Functional")
@@ -1232,4 +1261,18 @@ public class SequenceFeaturesTest
assertTrue(store.isOntologyTerm("junk", new String[] {}));
assertTrue(store.isOntologyTerm("junk", (String[]) null));
}
+
+ @Test(groups = "Functional")
+ public void testDeleteAll()
+ {
+ SequenceFeaturesI store = new SequenceFeatures();
+ assertFalse(store.hasFeatures());
+ store.deleteAll();
+ assertFalse(store.hasFeatures());
+ store.add(new SequenceFeature("Cath", "Desc", 12, 20, 0f, "Group"));
+ store.add(new SequenceFeature("Pfam", "Desc", 6, 12, 2f, "Group2"));
+ assertTrue(store.hasFeatures());
+ store.deleteAll();
+ assertFalse(store.hasFeatures());
+ }
}
diff --git a/test/jalview/ext/htsjdk/TestHtsContigDb.java b/test/jalview/ext/htsjdk/TestHtsContigDb.java
index 28c5cf0..69634a9 100644
--- a/test/jalview/ext/htsjdk/TestHtsContigDb.java
+++ b/test/jalview/ext/htsjdk/TestHtsContigDb.java
@@ -44,12 +44,12 @@ public class TestHtsContigDb
@Test(groups = "Functional")
public final void testGetSequenceProxy() throws Exception
{
- String pathname = "test/jalview/ext/htsjdk/pgmb.fasta";
+ String pathname = "test/jalview/ext/htsjdk/pgmB.fasta";
HtsContigDb db = new HtsContigDb("ADB", new File(pathname));
-
+
assertTrue(db.isValid());
assertTrue(db.isIndexed()); // htsjdk opens the .fai file
-
+
SequenceI sq = db.getSequenceProxy("Deminut");
assertNotNull(sq);
assertEquals(sq.getLength(), 606);
@@ -60,7 +60,7 @@ public class TestHtsContigDb
sq = db.getSequenceProxy("PPL_06716");
assertNotNull(sq);
assertEquals(sq.getLength(), 602);
-
+
// dict = db.getDictionary(f, truncate))
}
@@ -73,7 +73,7 @@ public class TestHtsContigDb
expectedExceptions = java.lang.IllegalArgumentException.class)
public final void testGetSequenceProxy_indexed()
{
- String pathname = "test/jalview/ext/htsjdk/pgmb.fasta.fai";
+ String pathname = "test/jalview/ext/htsjdk/pgmB.fasta.fai";
new HtsContigDb("ADB", new File(pathname));
fail("Expected exception opening .fai file");
}
@@ -92,17 +92,19 @@ public class TestHtsContigDb
@Test(groups = "Functional")
public void testCreateFastaSequenceIndex() throws IOException
{
- File fasta = new File("test/jalview/ext/htsjdk/pgmb.fasta");
-
+ File fasta = new File("test/jalview/ext/htsjdk/pgmB.fasta");
+
/*
* create .fai with no overwrite fails if it exists
*/
- try {
+ try
+ {
HtsContigDb.createFastaSequenceIndex(fasta.toPath(), false);
fail("Expected exception");
} catch (IOException e)
{
- // expected
+ // we expect an IO Exception because the pgmB.fasta.fai exists, since it
+ // was checked it in.
}
/*
diff --git a/test/jalview/ext/jmol/JmolParserTest.java b/test/jalview/ext/jmol/JmolParserTest.java
index f5e637c..bcad464 100644
--- a/test/jalview/ext/jmol/JmolParserTest.java
+++ b/test/jalview/ext/jmol/JmolParserTest.java
@@ -62,9 +62,9 @@ public class JmolParserTest
* 1GAQ has been reduced to alpha carbons only
* 1QCF is the full PDB file including headers, HETATM etc
*/
- String[] testFile = new String[] { "./examples/1GAQ.txt",
+ String[] testFile = new String[] { "./examples/1gaq.txt",
"./test/jalview/ext/jmol/1xyz.pdb",
- "./test/jalview/ext/jmol/1qcf.pdb" };
+ "./test/jalview/ext/jmol/1QCF.pdb" };
//@formatter:off
// a modified and very cut-down extract of 4UJ4
@@ -130,17 +130,17 @@ public class JmolParserTest
JmolParser jtest = new JmolParser(pdbStr, DataSourceType.FILE);
Vector seqs = jtest.getSeqs(), mcseqs = mctest.getSeqs();
- assertTrue(
- "No sequences extracted from testfile\n"
- + (jtest.hasWarningMessage() ? jtest.getWarningMessage()
- : "(No warnings raised)"), seqs != null
- && seqs.size() > 0);
+ assertTrue("No sequences extracted from testfile\n"
+ + (jtest.hasWarningMessage() ? jtest.getWarningMessage()
+ : "(No warnings raised)"),
+ seqs != null && seqs.size() > 0);
for (SequenceI sq : seqs)
{
- assertEquals("JMol didn't process " + pdbStr
- + " to the same sequence as MCView",
- sq.getSequenceAsString(), mcseqs.remove(0)
- .getSequenceAsString());
+ assertEquals(
+ "JMol didn't process " + pdbStr
+ + " to the same sequence as MCView",
+ sq.getSequenceAsString(),
+ mcseqs.remove(0).getSequenceAsString());
AlignmentI al = new Alignment(new SequenceI[] { sq });
validateSecStrRows(al);
}
@@ -175,13 +175,15 @@ public class JmolParserTest
private void checkFirstAAIsAssoc(SequenceI sq)
{
- assertTrue("No secondary structure assigned for protein sequence for "
- + sq.getName(),
+ assertTrue(
+ "No secondary structure assigned for protein sequence for "
+ + sq.getName(),
sq.getAnnotation() != null && sq.getAnnotation().length >= 1
&& sq.getAnnotation()[0].hasIcons);
assertTrue(
"Secondary structure not associated for sequence "
- + sq.getName(), sq.getAnnotation()[0].sequenceRef == sq);
+ + sq.getName(),
+ sq.getAnnotation()[0].sequenceRef == sq);
}
/**
@@ -194,7 +196,8 @@ public class JmolParserTest
{
PDBfile mctest = new PDBfile(false, false, false,
pastePDBDataWithChainBreak, DataSourceType.PASTE);
- JmolParser jtest = new JmolParser(pastePDBDataWithChainBreak, DataSourceType.PASTE);
+ JmolParser jtest = new JmolParser(pastePDBDataWithChainBreak,
+ DataSourceType.PASTE);
Vector seqs = jtest.getSeqs();
Vector mcseqs = mctest.getSeqs();
@@ -216,8 +219,7 @@ public class JmolParserTest
{
PDBfile mctest = new PDBfile(false, false, false, pdbWithAltLoc,
DataSourceType.PASTE);
- JmolParser jtest = new JmolParser(pdbWithAltLoc,
- DataSourceType.PASTE);
+ JmolParser jtest = new JmolParser(pdbWithAltLoc, DataSourceType.PASTE);
Vector seqs = jtest.getSeqs();
Vector mcseqs = mctest.getSeqs();
diff --git a/test/jalview/ext/rbvi/chimera/ChimeraConnect.java b/test/jalview/ext/rbvi/chimera/ChimeraConnect.java
index 4d904cf..99394dc 100644
--- a/test/jalview/ext/rbvi/chimera/ChimeraConnect.java
+++ b/test/jalview/ext/rbvi/chimera/ChimeraConnect.java
@@ -41,7 +41,7 @@ public class ChimeraConnect
JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
}
- @Test(groups = { "Functional" })
+ @Test(groups = { "External" })
public void testLaunchAndExit()
{
final StructureManager structureManager = new StructureManager(true);
diff --git a/test/jalview/gui/AlignViewportTest.java b/test/jalview/gui/AlignViewportTest.java
index 5ed0cac..7801250 100644
--- a/test/jalview/gui/AlignViewportTest.java
+++ b/test/jalview/gui/AlignViewportTest.java
@@ -277,7 +277,7 @@ public class AlignViewportTest
* Test for JAL-1306 - conservation thread should run even when only Quality
* (and not Conservation) is enabled in Preferences
*/
- @Test(groups = { "Functional" })
+ @Test(groups = { "Functional" }, timeOut=2000)
public void testUpdateConservation_qualityOnly()
{
Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
@@ -292,7 +292,24 @@ public class AlignViewportTest
Boolean.FALSE.toString());
AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
"examples/uniref50.fa", DataSourceType.FILE);
- AlignmentAnnotation[] anns = af.viewport.getAlignment()
+
+ /*
+ * wait for Conservation thread to complete
+ */
+ AlignViewport viewport = af.getViewport();
+ synchronized (this)
+ {
+ while (viewport.getAlignmentConservationAnnotation() != null)
+ {
+ try
+ {
+ wait(50);
+ } catch (InterruptedException e)
+ {
+ }
+ }
+ }
+ AlignmentAnnotation[] anns = viewport.getAlignment()
.getAlignmentAnnotation();
assertNotNull("No annotations found", anns);
assertEquals("More than one annotation found", 1, anns.length);
diff --git a/test/jalview/gui/AlignmentPanelTest.java b/test/jalview/gui/AlignmentPanelTest.java
index 2819dbf..e84b87a 100644
--- a/test/jalview/gui/AlignmentPanelTest.java
+++ b/test/jalview/gui/AlignmentPanelTest.java
@@ -222,7 +222,7 @@ public class AlignmentPanelTest
/**
* Test that update layout reverts to original (unwrapped) values for endRes
- * and endSeq when switching from wrapped to unwrapped mode (JAL-2739)
+ * when switching from wrapped back to unwrapped mode (JAL-2739)
*/
@Test(groups = "Functional")
public void TestUpdateLayout_endRes()
@@ -235,14 +235,14 @@ public class AlignmentPanelTest
af.alignPanel.getAlignViewport().setWrapAlignment(true);
af.alignPanel.updateLayout();
- // endRes changes
+ // endRes has changed
assertNotEquals(ranges.getEndRes(), endres);
// unwrap
af.alignPanel.getAlignViewport().setWrapAlignment(false);
af.alignPanel.updateLayout();
- // endRes and endSeq back to original values
+ // endRes back to original value
assertEquals(ranges.getEndRes(), endres);
}
diff --git a/test/jalview/gui/FreeUpMemoryTest.java b/test/jalview/gui/FreeUpMemoryTest.java
index e93bfac..9b21274 100644
--- a/test/jalview/gui/FreeUpMemoryTest.java
+++ b/test/jalview/gui/FreeUpMemoryTest.java
@@ -1,6 +1,7 @@
package jalview.gui;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import jalview.analysis.AlignmentGenerator;
@@ -11,6 +12,7 @@ import jalview.datamodel.SequenceGroup;
import jalview.io.DataSourceType;
import jalview.io.FileLoader;
+import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
@@ -18,6 +20,8 @@ import java.io.PrintStream;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import junit.extensions.PA;
+
public class FreeUpMemoryTest
{
private static final int ONE_MB = 1000 * 1000;
@@ -30,16 +34,12 @@ public class FreeUpMemoryTest
{
Jalview.main(new String[] { "-nonews", "-props",
"test/jalview/testProps.jvprops" });
- Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS",
- Boolean.TRUE.toString());
- Cache.applicationProperties.setProperty("SHOW_QUALITY",
- Boolean.TRUE.toString());
- Cache.applicationProperties.setProperty("SHOW_CONSERVATION",
- Boolean.TRUE.toString());
- Cache.applicationProperties.setProperty("SHOW_OCCUPANCY",
- Boolean.TRUE.toString());
- Cache.applicationProperties.setProperty("SHOW_IDENTITY",
- Boolean.TRUE.toString());
+ String True = Boolean.TRUE.toString();
+ Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", True);
+ Cache.applicationProperties.setProperty("SHOW_QUALITY", True);
+ Cache.applicationProperties.setProperty("SHOW_CONSERVATION", True);
+ Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", True);
+ Cache.applicationProperties.setProperty("SHOW_IDENTITY", True);
}
/**
@@ -84,18 +84,19 @@ public class FreeUpMemoryTest
protected void checkUsedMemory(long expectedMax)
{
/*
- * request garbage collection and wait briefly for it to run;
+ * request garbage collection and wait for it to run;
* NB there is no guarantee when, or whether, it will do so
+ * wait time depends on JRE/processor, generous allowance here
*/
System.gc();
- waitFor(100);
+ waitFor(1500);
/*
* a second gc() call should not be necessary - but it is!
* the test passes with it, and fails without it
*/
System.gc();
- waitFor(100);
+ waitFor(1500);
/*
* check used memory is 'reasonably low'
@@ -142,6 +143,20 @@ public class FreeUpMemoryTest
}
/*
+ * open an Overview window
+ */
+ af.overviewMenuItem_actionPerformed(null);
+ assertNotNull(af.alignPanel.overviewPanel);
+
+ /*
+ * exercise the pop-up menu in the Overview Panel (JAL-2864)
+ */
+ Object[] args = new Object[] {
+ new MouseEvent(af, 0, 0, 0, 0, 0, 1, true) };
+ PA.invokeMethod(af.alignPanel.overviewPanel,
+ "showPopupMenu(java.awt.event.MouseEvent)", args);
+
+ /*
* set a selection group - potential memory leak if it retains
* a reference to the alignment
*/
diff --git a/test/jalview/gui/ScalePanelTest.java b/test/jalview/gui/ScalePanelTest.java
new file mode 100644
index 0000000..91b541c
--- /dev/null
+++ b/test/jalview/gui/ScalePanelTest.java
@@ -0,0 +1,58 @@
+package jalview.gui;
+
+import static org.testng.Assert.assertTrue;
+
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceGroup;
+import jalview.datamodel.SequenceI;
+
+import java.awt.event.MouseEvent;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+public class ScalePanelTest
+{
+ @BeforeClass(alwaysRun = true)
+ public void setUpJvOptionPane()
+ {
+ JvOptionPane.setInteractiveMode(false);
+ JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+ }
+
+ @Test(groups = "Functional")
+ public void testPreventNegativeStartColumn()
+ {
+ SequenceI seq1 = new Sequence("Seq1", "MATRESS");
+ SequenceI seq2 = new Sequence("Seq2", "MADNESS");
+ AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
+
+ AlignFrame alignFrame = new AlignFrame(al, al.getWidth(),
+ al.getHeight());
+ ScalePanel scalePanel = new ScalePanel(
+ alignFrame.getViewport(), alignFrame.alignPanel
+ );
+
+ MouseEvent mouse = new MouseEvent(
+ scalePanel, 0, 1, 0, 4, 0, 1, false
+ );
+ scalePanel.mousePressed(mouse);
+ scalePanel.mouseDragged(mouse);
+
+ // simulate dragging selection leftwards beyond the sequences giving
+ // negative X
+ mouse = new MouseEvent(scalePanel, 0, 1, 0, -30, 0, 1, false);
+
+ scalePanel.mouseReleased(mouse);
+
+ SequenceGroup sg = scalePanel.av.getSelectionGroup();
+ int startCol = sg.getStartRes();
+
+ assertTrue(startCol >= 0);
+
+
+ }
+
+}
diff --git a/test/jalview/io/IdentifyFileTest.java b/test/jalview/io/IdentifyFileTest.java
index dd4f6ba..5be7968 100644
--- a/test/jalview/io/IdentifyFileTest.java
+++ b/test/jalview/io/IdentifyFileTest.java
@@ -94,7 +94,7 @@ public class IdentifyFileTest
{ "examples/plantfdx.fa", FileFormat.Fasta },
{ "examples/dna_interleaved.phy", FileFormat.Phylip },
{ "examples/2GIS.pdb", FileFormat.PDB },
- { "examples/rf00031_folded.stk", FileFormat.Stockholm },
+ { "examples/RF00031_folded.stk", FileFormat.Stockholm },
{ "examples/testdata/test.rnaml", FileFormat.Rnaml },
{ "examples/testdata/test.aln", FileFormat.Clustal },
{ "examples/testdata/test.pfam", FileFormat.Pfam },
diff --git a/test/jalview/io/cache/JvCacheableInputBoxTest.java b/test/jalview/io/cache/JvCacheableInputBoxTest.java
index dfd7973..010a4b2 100644
--- a/test/jalview/io/cache/JvCacheableInputBoxTest.java
+++ b/test/jalview/io/cache/JvCacheableInputBoxTest.java
@@ -55,10 +55,9 @@ public class JvCacheableInputBoxTest
cacheBox.updateCache();
try
{
- // This 1ms delay is essential to prevent the
- // assertion below from executing before
- // cacheBox.updateCache() finishes updating the cache
- Thread.sleep(100);
+ // This delay is to let
+ // cacheBox.updateCache() finish updating the cache
+ Thread.sleep(200);
} catch (InterruptedException e)
{
e.printStackTrace();
diff --git a/test/jalview/schemes/FeatureColourTest.java b/test/jalview/schemes/FeatureColourTest.java
index 2eb718b..a96caec 100644
--- a/test/jalview/schemes/FeatureColourTest.java
+++ b/test/jalview/schemes/FeatureColourTest.java
@@ -523,11 +523,60 @@ public class FeatureColourTest
assertFalse(fc.hasThreshold());
assertEquals(Color.RED, fc.getMinColour());
assertEquals(Color.GREEN, fc.getMaxColour());
+ assertEquals(Color.RED, fc.getNoColour());
assertEquals(10f, fc.getMin());
assertEquals(20f, fc.getMax());
assertTrue(fc.isAutoScaled());
/*
+ * the same, with 'no value colour' specified as max
+ */
+ fc = FeatureColour
+ .parseJalviewFeatureColour("red|green|novaluemax|10.0|20.0");
+ assertEquals(Color.RED, fc.getMinColour());
+ assertEquals(Color.GREEN, fc.getMaxColour());
+ assertEquals(Color.GREEN, fc.getNoColour());
+ assertEquals(10f, fc.getMin());
+ assertEquals(20f, fc.getMax());
+
+ /*
+ * the same, with 'no value colour' specified as min
+ */
+ fc = FeatureColour
+ .parseJalviewFeatureColour("red|green|novalueMin|10.0|20.0");
+ assertEquals(Color.RED, fc.getMinColour());
+ assertEquals(Color.GREEN, fc.getMaxColour());
+ assertEquals(Color.RED, fc.getNoColour());
+ assertEquals(10f, fc.getMin());
+ assertEquals(20f, fc.getMax());
+
+ /*
+ * the same, with 'no value colour' specified as none
+ */
+ fc = FeatureColour
+ .parseJalviewFeatureColour("red|green|novaluenone|10.0|20.0");
+ assertEquals(Color.RED, fc.getMinColour());
+ assertEquals(Color.GREEN, fc.getMaxColour());
+ assertNull(fc.getNoColour());
+ assertEquals(10f, fc.getMin());
+ assertEquals(20f, fc.getMax());
+
+ /*
+ * the same, with invalid 'no value colour'
+ */
+ try
+ {
+ fc = FeatureColour
+ .parseJalviewFeatureColour("red|green|blue|10.0|20.0");
+ fail("expected exception");
+ } catch (IllegalArgumentException e)
+ {
+ assertEquals(
+ "Couldn't parse the minimum value for graduated colour ('blue')",
+ e.getMessage());
+ }
+
+ /*
* graduated colour (explicitly by 'score') (no threshold)
*/
fc = FeatureColour
diff --git a/test/jalview/viewmodel/styles/ViewStyleTest.java b/test/jalview/viewmodel/styles/ViewStyleTest.java
index 26c3574..885cb71 100644
--- a/test/jalview/viewmodel/styles/ViewStyleTest.java
+++ b/test/jalview/viewmodel/styles/ViewStyleTest.java
@@ -68,7 +68,7 @@ public class ViewStyleTest
for (Field field : fields)
{
field.setAccessible(true);
- if (!copyConstructorIgnores(field.getName()))
+ if (!copyConstructorIgnores(field))
{
changeValue(vs1, field);
}
@@ -78,37 +78,49 @@ public class ViewStyleTest
for (Field field1 : fields)
{
- final Object value1 = field1.get(vs1);
- final Object value2 = field1.get(vs2);
- String msg = "Mismatch in " + field1.getName() + "(" + value1 + "/"
+ if (!copyConstructorIgnores(field1))
+ {
+ final Object value1 = field1.get(vs1);
+ final Object value2 = field1.get(vs2);
+ String msg = "Mismatch in " + field1.getName() + "(" + value1 + "/"
+ value2 + ") - not set in copy constructor?";
- assertEquals(msg, value1, value2);
+ assertEquals(msg, value1, value2);
+ }
}
assertEquals("Hashcode not equals", vs1.hashCode(), vs2.hashCode());
}
/**
- * Add any field names in here that we expect to be ignored by the copy
- * constructor
+ * Add tests here for any fields that we expect to be ignored by
+ * the copy constructor
*
- * @param name
+ * @param field
* @return
*/
- private boolean copyConstructorIgnores(String name)
+ private boolean copyConstructorIgnores(Field field)
{
/*
- * currently none!
+ * ignore instrumentation added by jacoco for test coverage
*/
+ if (field.isSynthetic())
+ {
+ return true;
+ }
+ if (field.getType().toString().contains("com_atlassian_clover"))
+ {
+ return true;
+ }
+
return false;
}
-
+
/**
- * Change the value of one field in a ViewStyle object
- *
- * @param vs
- * @param field
- * @throws IllegalAccessException
- */
+ * Change the value of one field in a ViewStyle object
+ *
+ * @param vs
+ * @param field
+ * @throws IllegalAccessException
+ */
protected void changeValue(ViewStyle vs, Field field)
throws IllegalAccessException
{
@@ -210,19 +222,22 @@ public class ViewStyleTest
Field[] fields = ViewStyle.class.getDeclaredFields();
for (Field field : fields)
{
- field.setAccessible(true);
- Object oldValue = field.get(vs2);
- changeValue(vs2, field);
- assertFalse("equals method ignores " + field.getName(),
+ if (!copyConstructorIgnores(field))
+ {
+ field.setAccessible(true);
+ Object oldValue = field.get(vs2);
+ changeValue(vs2, field);
+ assertFalse("equals method ignores " + field.getName(),
vs1.equals(vs2));
- if (vs1.hashCode() == vs2.hashCode())
- {
- // uncomment next line to see which fields hashCode ignores
- // System.out.println("hashCode ignores " + field.getName());
+ if (vs1.hashCode() == vs2.hashCode())
+ {
+ // uncomment next line to see which fields hashCode ignores
+ // System.out.println("hashCode ignores " + field.getName());
+ }
+ // restore original value before testing the next field
+ field.set(vs2, oldValue);
}
- // restore original value before testing the next field
- field.set(vs2, oldValue);
}
}
}
diff --git a/utils/checkstyle/README.txt b/utils/checkstyle/README.txt
index e38064e..54f4e48 100644
--- a/utils/checkstyle/README.txt
+++ b/utils/checkstyle/README.txt
@@ -1,6 +1,8 @@
Checkstyle for Jalview
----------------------
+See
+https://issues.jalview.org/browse/JAL-1854
http://checkstyle.sourceforge.net/
GNU LGPL
@@ -9,8 +11,16 @@ To get the Eclipse Checkstyle plugin
- Help | Eclipse Marketplace
- search for checkstyle
- install eclipse-cs checkstyle plugin
-The current version is 6.19.1 (August 2016).
+Change Log
+----------
+See http://checkstyle.sourceforge.net/releasenotes.html
+Aug 2016 Initial version used is 6.19.1
+Dec 2018 Updated to 8.12.0 (latest on Eclipse Marketplace, 8.15 is latest release)
+ SuppressionCommentFilter relocated (changed in 8.1)
+ FileContentsHolder removed (changed in 8.2)
+ Updates to import-control.xml for code changes (htsjdk, stackoverflowusers)
+
Config
------
@@ -37,6 +47,7 @@ How to use checkstyle
Option 2: on demand on selected code
- right-click on a class or package and Checkstyle | Check code with checkstyle
- (or Clear Checkstyle violations to remove checkstyle warnings)
+ - recommended to use this as a QA step when changing or reviewing code
Checkstyle rules
----------------
@@ -64,11 +75,11 @@ Suppressing findings
Tips
----
Sometimes checkstyle needs a kick before it will refresh its findings.
- A whitespace edit in checkstyle.xml usually does this. There may be better ways.
+ Click the 'refresh' icon at top right in Eclipse | Preferences | Checkstyle.
Invalid configuration files may result in checkstyle failing with an error reported
in the Eclipse log file.
- Help | Installation Details | Configuration takes you to a screen with a
+ Eclipse | About | Installation Details | Configuration takes you to a screen with a
'View Error Log' button.
Sometimes checkstyle can fail silently. Try 'touching' (editing) config files, failing
diff --git a/utils/checkstyle/checkstyle-suppress.xml b/utils/checkstyle/checkstyle-suppress.xml
index 122b8d0..29a3047 100644
--- a/utils/checkstyle/checkstyle-suppress.xml
+++ b/utils/checkstyle/checkstyle-suppress.xml
@@ -26,6 +26,7 @@
+
-
-
-
-
-
-
-
@@ -54,10 +42,16 @@
-
+
+
+
+
+
diff --git a/utils/checkstyle/import-control.xml b/utils/checkstyle/import-control.xml
index c47aaec..478966f 100644
--- a/utils/checkstyle/import-control.xml
+++ b/utils/checkstyle/import-control.xml
@@ -94,6 +94,7 @@
+
@@ -116,6 +117,9 @@
+
+
+
|