Select Git revision
qualifier_mapping
Selection.java 26.20 KiB
/* Selection.java
*
* created: Fri Nov 6 1998
*
* This file is part of Artemis
*
* Copyright (C) 1998,1999,2000 Genome Research Limited
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/Selection.java,v 1.3 2008-06-26 09:36:50 tjc Exp $
*/
package uk.ac.sanger.artemis;
import uk.ac.sanger.artemis.sequence.*;
import uk.ac.sanger.artemis.util.OutOfRangeException;
import uk.ac.sanger.artemis.io.Range;
import uk.ac.sanger.artemis.io.RangeVector;
import java.io.*;
import java.util.Vector;
import java.awt.datatransfer.*;
/**
* Objects of this class hold the features/feature segments or entries that
* the user has selected.
*
* @author Kim Rutherford
* @version $Id: Selection.java,v 1.3 2008-06-26 09:36:50 tjc Exp $
*
**/
public class Selection
implements FeatureChangeListener, EntryChangeListener,
Transferable, ClipboardOwner {
/**
* Create a new Selection object.
* @param clipboard The system clipboard reference. This may be null if
* this Selection should not set the system clipboard.
**/
public Selection (final Clipboard clipboard) {
this.clipboard = clipboard;
}
private final DataFlavor flavors [] = {
DataFlavor.stringFlavor,
// deprecated at 1.3
// DataFlavor.plainTextFlavor
};
/**
* Returns the array of the data flavors that this object can provide.
**/
public synchronized DataFlavor[] getTransferDataFlavors () {
return flavors;
}
/**
* Returns whether the requested flavor is supported by this object.
* @param flavor the requested flavor for the data
**/
public boolean isDataFlavorSupported (final DataFlavor flavor) {
return
flavor.equals (DataFlavor.stringFlavor)
// deprecated at 1.3
// || flavor.equals (DataFlavor.plainTextFlavor)
;
}
/**
* If the data was requested in the "java.lang.String" flavor, return the
* String representing the selection, else throw an
* UnsupportedFlavorException.
* @param flavor the requested flavor for the data
**/
public synchronized Object getTransferData (final DataFlavor flavor)
throws UnsupportedFlavorException, IOException {
if (flavor.equals (DataFlavor.stringFlavor)) {
return getSelectionText ();
// deprecated at 1.3
// } else if (flavor.equals (DataFlavor.plainTextFlavor)) {
// return new StringReader (getSelectionText ());
} else {
throw new UnsupportedFlavorException (flavor);
}
}
/**
* Implementation of the ClipboardOwner interface.
**/
public void lostOwnership(final Clipboard clipboard,
final Transferable contents) {
}
/**
* Returns this Selection in a String.
**/
public String getSelectionText () {
final FeatureVector selection_features = getAllFeatures ();
final StringBuffer buffer = new StringBuffer ();
final MarkerRange marker_range = getMarkerRange ();
if (marker_range != null) {
final String selection_bases = Strand.markerRangeBases (marker_range);
buffer.append (selection_bases);
}
for (int i = 0 ; i < selection_features.size () && i < 50 ; ++i) {
buffer.append (selection_features.elementAt (i).toString ());
}
return buffer.toString ();
}
/**
* Implementation of the FeatureChangeListener interface. We need to
* listen to feature change events so a SelectionChangeEvent can be sent if
* a selected feature is changed.
* @param event The change event.
**/
public void featureChanged (FeatureChangeEvent event) {
if (segments == null && features.contains (event.getFeature ()) ||
getAllFeatures ().contains (event.getFeature ())) {
if (event.getType () == FeatureChangeEvent.QUALIFIER_CHANGED ||
event.getType () == FeatureChangeEvent.KEY_CHANGED) {
// no need to reset the cache in this case
/*final SelectionChangeEvent selection_change_event =
new SelectionChangeEvent (this, SelectionChangeEvent.OBJECT_CHANGED);
fireAction (selection_listener_list, selection_change_event);*/
} else {
changeSelection (SelectionChangeEvent.OBJECT_CHANGED);
}
}
}
/**
* Implementation of the EntryChangeListener interface. We listen to
* EntryChange events so that we remove feature from the selection when
* they are removed from the Entry.
**/
public void entryChanged (final EntryChangeEvent event) {
switch (event.getType ()) {
case EntryChangeEvent.FEATURE_DELETED:
features.remove (event.getFeature ());
if (contains (event.getFeature ())) {
// we have a segment of the feature in the Selection -
// this will fire a SelectionChangeEvent
removeSegmentsOf (event.getFeature ());
} else {
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
break;
}
}
/**
* Adds the specified event listener to receive selection change events
* from this object.
* @param l the event change listener.
**/
public void addSelectionChangeListener (final SelectionChangeListener l) {
selection_listener_list.addElement (l);
}
/**
* Removes the specified event listener so that it no longer receives
* selection change events from this object.
* @param l the event change listener.
**/
public void removeSelectionChangeListener (final SelectionChangeListener l) {
selection_listener_list.removeElement (l);
}
/**
* Broadcast an event to notify SelectionChange event listeners that the
* selection has changed.
* @param type The type to use for the new event (see SelectionChangeEvent
* for details).
**/
private void changeSelection (final int type) {
resetCache ();
final SelectionChangeEvent event =
new SelectionChangeEvent (this, type);
fireAction (selection_listener_list, event);
if (clipboard != null) {
clipboard.setContents (this, this);
}
}
/**
* Add a Feature to the selection if it isn't there already.
**/
public void add (final Feature feature) {
if (addWithoutEvent (feature)) {
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
}
/**
* Add the given Feature objects to the selection, and then send an
* appropriate event to the SelectionChangeEvent listeners.
**/
public void add (final FeatureVector features) {
for (int i = 0 ; i < features.size () ; ++i) {
addWithoutEvent (features.elementAt (i));
}
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
/**
* Add a Feature to the selection if it isn't there already, but don't send
* an event to the SelectionChangeEvent listeners.
* @return true if and only if the segment with not already in the
* selection.
**/
private boolean addWithoutEvent (final Feature feature) {
if (features.contains (feature)) {
return false;
// do nothing
} else {
features.add (feature);
return true;
}
}
/**
* Add a FeatureSegment to the selection if it isn't there already, and
* send an event to the SelectionChangeEvent listeners..
**/
public void add (final FeatureSegment segment) {
if (addWithoutEvent (segment)) {
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
}
/**
* Add a FeatureSegment to the selection if it isn't there already, but
* don't send an event to the SelectionChangeEvent listeners.
* @return true if and only if the segment with not already in the
* selection.
**/
private boolean addWithoutEvent (final FeatureSegment segment) {
if (segments.contains (segment)) {
return false;
// do nothing
} else {
segments.addElement (segment);
return true;
}
}
/**
* Remove all the objects from the selection, add the given Feature object
* to the selection, and then send an appropriate event to the
* SelectionChangeEvent listeners.
**/
public void set (final Feature feature) {
clearWithoutEvent ();
addWithoutEvent (feature);
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
/**
* Remove all the objects from the selection, add the given FeatureSegment
* object to the selection, and then send an appropriate event to the
* SelectionChangeEvent listeners.
**/
public void set (final FeatureSegment feature_segment) {
clearWithoutEvent ();
addWithoutEvent (feature_segment);
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
/**
* Remove all the objects from the selection, add the given Feature objects
* to the selection, and then send an appropriate event to the
* SelectionChangeEvent listeners.
**/
public void set (final FeatureVector features) {
clearWithoutEvent ();
for (int i = 0 ; i < features.size () ; ++i) {
addWithoutEvent (features.elementAt (i));
}
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
/**
* Set the MarkerRange object that this Selection holds and remove all
* other objects.
**/
public void setMarkerRange (final MarkerRange marker_range) {
if (this.marker_range != marker_range) {
final MarkerRange old_marker_range = this.marker_range;
clearWithoutEvent ();
this.marker_range = marker_range;
if (old_marker_range == null) {
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
} else {
changeSelection (SelectionChangeEvent.OBJECT_CHANGED);
}
}
}
/**
* Return true if and only if there is nothing in the selection.
**/
public boolean isEmpty () {
if (features.size () == 0 &&
segments.size () == 0 &&
marker_range == null) {
return true;
} else {
return false;
}
}
/**
* Remove all the objects from the selection, and send an event to the
* SelectionChangeEvent listeners.
**/
public void clear () {
if (!isEmpty ()) {
clearWithoutEvent ();
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
}
/**
* Remove all the objects from the selection, but don't send an event to
* the SelectionChangeEvent listeners.
**/
private void clearWithoutEvent () {
features.removeAllElements ();
segments.removeAllElements ();
marker_range = null;
}
/**
* Remove a Feature from the selection.
**/
public void remove (final Feature feature) {
if (features.remove (feature)) {
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
}
/**
* Remove the FeatureSegments of the given Feature from the selection.
**/
public void removeSegmentsOf (final Feature feature) {
final FeatureSegmentVector segments = feature.getSegments ();
for (int segment_index = 0 ;
segment_index < segments.size () ;
++segment_index) {
final FeatureSegment this_segment = segments.elementAt (segment_index);
remove (this_segment);
}
}
/**
* Remove a FeatureSegment from the selection.
**/
public void remove (final FeatureSegment segment) {
if (segments.removeElement (segment)) {
changeSelection (SelectionChangeEvent.SELECTION_CHANGED);
}
}
/**
* Return true if this selection contains the given Feature.
**/
public boolean contains (final Feature feature) {
if (getSelectedFeatures ().contains (feature)) {
return true;
}
for (int i = 0 ; i < segments.size () ; ++i) {
final FeatureSegment this_segment = segments.elementAt (i);
final Feature this_feature = this_segment.getFeature ();
if (feature == this_feature) {
return true;
}
}
return false;
}
/**
* Return true if this selection contains the given Feature.
**/
public boolean contains (final FeatureSegment feature_segment) {
if (getAllSegments ().indexOf (feature_segment) == -1) {
return false;
} else {
return true;
}
}
/**
* Return a Range object that exactly covers the current selection.
* Returns null if and only if nothing is selected.
**/
public Range getSelectionRange () {
final Marker lowest_base_marker = getLowestBaseOfSelection ();
final Marker highest_base_marker = getHighestBaseOfSelection ();
if (lowest_base_marker == null || highest_base_marker == null) {
return null;
} else {
final Strand strand = lowest_base_marker.getStrand ();
final int first_postion = lowest_base_marker.getRawPosition ();
final int last_postion = highest_base_marker.getRawPosition ();
try {
return new Range (first_postion, last_postion);
} catch (OutOfRangeException e) {
throw new Error ("internal error - unexpected exception: " + e);
}
}
}
/**
* Return a Range object for each of the selected objects.
**/
public RangeVector getSelectionRanges () {
final RangeVector return_ranges = new RangeVector ();
if (getSelectedFeatures ().size () > 0 ||
getSelectedSegments ().size () > 0) {
final FeatureSegmentVector segments = getAllSegments ();
for (int i = 0 ; i < segments.size () ; ++i) {
final FeatureSegment this_segment = segments.elementAt (i);
return_ranges.add (this_segment.getRawRange ());
}
} else {
final MarkerRange marker_range = getMarkerRange ();
if (marker_range != null) {
return_ranges.add (marker_range.getRawRange ());
}
}
return return_ranges;
}
/**
* Return a Marker for the base in this selection that is closest to the
* first base of the sequence, or null if nothing is selected.
**/
public Marker getLowestBaseOfSelection () {
if (lowest_base_marker != null) {
return lowest_base_marker;
}
if (getSelectedFeatures ().size () > 0 ||
getSelectedSegments ().size () > 0) {
final FeatureSegmentVector segments = getAllSegments ();
int current_min = 99999999;
Marker current_min_marker = null;
for (int i = 0 ; i < segments.size () ; ++i) {
final FeatureSegment this_segment = segments.elementAt (i);
final Marker start_marker = this_segment.getStart ();
if (start_marker.getRawPosition () < current_min) {
current_min = start_marker.getRawPosition ();
current_min_marker = start_marker;
}
final Marker end_marker = this_segment.getEnd ();
if (end_marker.getRawPosition () < current_min) {
current_min = end_marker.getRawPosition ();
current_min_marker = end_marker;
}
}
lowest_base_marker = current_min_marker;
} else {
final MarkerRange range = getMarkerRange ();
if (range == null) {
lowest_base_marker = null;
} else {
if (range.isForwardMarker ()) {
lowest_base_marker = range.getStart ();
} else {
lowest_base_marker = range.getEnd ();
}
}
}
return lowest_base_marker;
}
/**
* Return a Marker for the base in this selection that is closest to the
* last base of the sequence, or null if nothing is selected.
**/
public Marker getHighestBaseOfSelection () {
if (highest_base_marker != null) {
return highest_base_marker;
}
if (getSelectedFeatures ().size () > 0 ||
getSelectedSegments ().size () > 0) {
final FeatureSegmentVector segments = getAllSegments ();
int current_max = -1;
Marker current_max_marker = null;
for (int i = 0 ; i < segments.size () ; ++i) {
final FeatureSegment this_segment = segments.elementAt (i);
final Marker start_marker = this_segment.getStart ();
if (start_marker.getRawPosition () > current_max) {
current_max = start_marker.getRawPosition ();
current_max_marker = start_marker;
}
final Marker end_marker = this_segment.getEnd ();
if (end_marker.getRawPosition () > current_max) {
current_max = end_marker.getRawPosition ();
current_max_marker = end_marker;
}
}
highest_base_marker = current_max_marker;
} else {
final MarkerRange range = getMarkerRange ();
if (range == null) {
highest_base_marker = null;
} else {
if (range.isForwardMarker ()) {
highest_base_marker = range.getEnd ();
} else {
highest_base_marker = range.getStart ();
}
}
}
return highest_base_marker;
}
/**
* Return the first base of the MarkerRange (if it is set), or the first
* base of the selected features (if there are any selected features), or
* the first base of the selected FeatureSegment objects (if there are any
* selected FeatureSegment objects), otherwise null. If the selection
* contains features or segments from both strands, then the result of
* calling this method is the same as calling getLowestBaseOfSelection ().
**/
public Marker getStartBaseOfSelection () {
if (start_base_marker != null) {
return start_base_marker;
}
if (getSelectedFeatures ().size () > 0 ||
getSelectedSegments ().size () > 0) {
final FeatureSegmentVector segments = getAllSegments ();
boolean seen_forward_feature = false;
boolean seen_reverse_feature = false;
int current_min = 99999999;
FeatureSegment current_min_segment = null;
for (int i = 0 ; i < segments.size () ; ++i) {
final FeatureSegment this_segment = segments.elementAt (i);
if (this_segment.getFeature ().isForwardFeature ()) {
seen_forward_feature = true;
} else {
seen_reverse_feature = true;
}
if (seen_forward_feature && seen_reverse_feature) {
return getLowestBaseOfSelection ();
}
final Marker start_marker = this_segment.getStart ();
if (current_min_segment == null ||
start_marker.getPosition () < current_min) {
current_min = start_marker.getPosition ();
current_min_segment = this_segment;
}
}
if (current_min_segment == null) {
// there are no segments with real ranges
start_base_marker = null;
} else {
start_base_marker = current_min_segment.getStart ();
}
} else {
final MarkerRange range = getMarkerRange ();
if (range == null) {
start_base_marker = null;
} else {
start_base_marker = range.getStart ();
}
}
return start_base_marker;
}
/**
* Return the last base of the MarkerRange (if it is set), or the last base
* of the selected features (if there are any selected features), or the
* last base of the selected FeatureSegment objects (if there are any
* selected FeatureSegment objects), otherwise null. If the selection
* contains features or segments from both strands, then the result of
* calling this method is the same as calling getLowestBaseOfSelection ().
**/
public Marker getEndBaseOfSelection () {
if (end_base_marker != null) {
return end_base_marker;
}
if (getSelectedFeatures ().size () > 0 ||
getSelectedSegments ().size () > 0) {
final FeatureSegmentVector segments = getAllSegments ();
boolean seen_forward_feature = false;
boolean seen_reverse_feature = false;
int current_max = -1;
FeatureSegment current_max_segment = null;
for (int i = 0 ; i < segments.size () ; ++i) {
final FeatureSegment this_segment = segments.elementAt (i);
if (this_segment.getFeature ().isForwardFeature ()) {
seen_forward_feature = true;
} else {
seen_reverse_feature = true;
}
if (seen_forward_feature && seen_reverse_feature) {
return getHighestBaseOfSelection ();
}
final Marker start_marker = this_segment.getStart ();
if (current_max_segment == null ||
start_marker.getPosition () > current_max) {
current_max = start_marker.getPosition ();
current_max_segment = this_segment;
}
}
if (current_max_segment == null) {
// there are no segments with real ranges
end_base_marker = null;
} else {
end_base_marker = current_max_segment.getEnd ();
}
} else {
final MarkerRange range = getMarkerRange ();
if (range == null) {
end_base_marker = null;
} else {
end_base_marker = range.getEnd ();
}
}
return end_base_marker;
}
/**
* Return the bases of the selected features or the selected marker range.
**/
public String getSelectedBases () {
if (getMarkerRange () == null) {
final StringBuffer buffer = new StringBuffer ();
final FeatureVector all_features = getAllFeatures ();
for (int i = 0 ; i < all_features.size () ; ++i) {
final Feature this_feature = all_features.elementAt (i);
buffer.append (this_feature.getBases ());
}
return buffer.toString ();
} else {
return Strand.markerRangeBases (getMarkerRange ());
}
}
/**
* Return the object that was set with the setMarkerRange (MarkerRange)
* method.
**/
public MarkerRange getMarkerRange () {
return marker_range;
}
/**
* Return a vector of the Feature objects of this selection.
**/
public FeatureVector getSelectedFeatures () {
return features;
}
/**
* Return a vector of the FeatureSegment objects of this selection.
**/
public FeatureSegmentVector getSelectedSegments () {
return segments;
}
/**
* Return a vector of all the FeatureSegment objects of this selection.
* This will include the FeatureSegment objects that are owned by Feature
* objects in the selection.
**/
public FeatureSegmentVector getAllSegments () {
final FeatureSegmentVector return_segments = new FeatureSegmentVector ();
for (int i = 0 ; i < features.size () ; ++i) {
final Feature selection_feature = features.elementAt (i);
final FeatureSegmentVector segments = selection_feature.getSegments ();
for (int segment_index = 0 ;
segment_index < segments.size () ;
++segment_index) {
final FeatureSegment this_segment = segments.elementAt (segment_index);
if (!return_segments.contains (this_segment)) {
return_segments.addElement (this_segment);
}
}
}
for (int i = 0 ; i < segments.size () ; ++i) {
final FeatureSegment this_segment = segments.elementAt (i);
if (!return_segments.contains (this_segment)) {
return_segments.addElement (this_segment);
}
}
return return_segments;
}
/**
* Return a vector of all the Feature objects of this selection. This will
* include the Feature objects that own the FeatureSegment objects in the
* selection.
**/
public FeatureVector getAllFeatures () {
if (all_features == null) {
all_features = (FeatureVector) features.clone ();
for (int i = 0 ; i < segments.size () ; ++i) {
final FeatureSegment this_segment = segments.elementAt (i);
final Feature this_feature = this_segment.getFeature ();
if (!all_features.contains (this_feature)) {
all_features.add (this_feature);
}
}
}
return all_features;
}
/**
* Send an event to those object listening for it.
* @param listeners A Vector of the objects that the event should be sent
* to.
* @param event The event to send
**/
private void fireAction (final Vector listeners, final ChangeEvent event) {
final Vector targets;
// copied from a book - synchronising the whole method might cause a
// deadlock
synchronized (this) {
targets = (Vector) listeners.clone ();
}
for ( int i = 0 ; i < targets.size () ; ++i ) {
ChangeListener target = (ChangeListener) targets.elementAt (i);
if (event instanceof SelectionChangeEvent) {
final SelectionChangeListener selection_change_listener =
(SelectionChangeListener) target;
selection_change_listener.selectionChanged ((SelectionChangeEvent) event);
} else {
throw new Error ("Selection.fireAction () - unknown event");
}
}
}
/**
* Reset all cached values in the Selection.
**/
private void resetCache () {
lowest_base_marker = null;
highest_base_marker = null;
start_base_marker = null;
end_base_marker = null;
all_features = null;
}
/**
* A Vector containing the Feature objects that this selection currently
* holds.
**/
private FeatureVector features = new FeatureVector ();
/**
* A Vector containing the FeatureSegment objects that this selection
* currently holds.
**/
private FeatureSegmentVector segments = new FeatureSegmentVector ();
/**
* This is a cache used by getAllFeatures (). This will be set to null
* anytime the selection changes.
**/
private FeatureVector all_features = null;
/**
* Each Selection object can hold one MarkerRange.
**/
private MarkerRange marker_range = null;
/**
* A vector of those objects listening for selection change events.
**/
final private Vector selection_listener_list = new Vector ();
/**
* The system clipboard as passed to the constructor.
**/
final private Clipboard clipboard;
/**
* The cached value that getLowestBaseOfSelection () returns/sets.
**/
private Marker lowest_base_marker = null;
/**
* The cached value that getHighestBaseOfSelection () returns/sets.
**/
private Marker highest_base_marker = null;
/**
* The cached value that getStartBaseOfSelection () returns/sets.
**/
private Marker start_base_marker = null;
/**
* The cached value that getEndBaseOfSelection () returns/sets.
**/
private Marker end_base_marker = null;
}