Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
  • gh-pages
  • build-process-upgrade-merge
  • eb-apollo-generate_names
  • BT5_travis
  • hello_github
  • v18.1.0
  • v18.0.3
  • v18.0.2
  • v18.0.1
  • v18.0.0
  • v18.0.0-RC1
  • v17.0.1
  • v17.0.0
  • v16.0.17
  • v16.0.0
  • v15.0.0
  • v14.0.0
  • v13.2.0
19 results

SimpleEntryGroup.java

Blame
  • SimpleEntryGroup.java 30.54 KiB
    /* SimpleEntryGroup.java
     *
     * created: Wed Nov 11 1998
     *
     * This file is part of Artemis
     *
     * Copyright(C) 1998,1999,2000,2001,2002  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/SimpleEntryGroup.java,v 1.8 2008-06-11 15:15:23 tjc Exp $
     **/
    
    package uk.ac.sanger.artemis;
    
    import uk.ac.sanger.artemis.sequence.*;
    import uk.ac.sanger.artemis.components.MessageDialog;
    import uk.ac.sanger.artemis.io.GFFDocumentEntry;
    import uk.ac.sanger.artemis.io.IndexedGFFDocumentEntry;
    import uk.ac.sanger.artemis.io.Range;
    import uk.ac.sanger.artemis.io.StreamSequence;
    import uk.ac.sanger.artemis.io.SimpleDocumentEntry;
    import uk.ac.sanger.artemis.io.DatabaseDocumentEntry;
    import uk.ac.sanger.artemis.io.DocumentEntry;
    import uk.ac.sanger.artemis.util.DatabaseDocument;
    import uk.ac.sanger.artemis.util.ReadOnlyException;
    import uk.ac.sanger.artemis.util.OutOfRangeException;
    
    import java.util.Vector;
    import java.util.NoSuchElementException;
    
    /**
     *  This class implements a vector of Entry objects, with additional methods
     *  for querying and changing the feature tables of all the entries at
     *  once.  Objects of this class act a bit like single Entry objects.
     *
     *  @author Kim Rutherford
     *  @version $Id: SimpleEntryGroup.java,v 1.8 2008-06-11 15:15:23 tjc Exp $
     **/
    
    public class SimpleEntryGroup extends EntryVector
                                  implements EntryGroup
    {
    
      /** vector of those objects listening for entry change events. */
      final private Vector entry_group_listener_list = new Vector();
    
      /** vector of those objects listening for entry change events. */
      final private Vector entry_listener_list = new Vector();
    
      /** vector of those objects listening for feature change events. */
      final private Vector feature_listener_list = new Vector();
    
      /** vector of entries that are currently active (visible). */
      final private EntryVector active_entries = new EntryVector();
    
      /**
       *  The default Entry for this SimpleEntryGroup.  The "default" is the Entry
       *  where new features are created.
       **/
      private Entry default_entry = null;
    
      /** Bases object that was passed to the constructor. */
      private Bases bases;
    
      /** Incremented by ref(), decremented by unref(). */
      private int reference_count = 0;
    
      /** The ActionController of this EntryGroup (used for undo). */
      final private ActionController action_controller = new ActionController();
    
      /** 
       *  Create a new empty SimpleEntryGroup object.
       **/
      public SimpleEntryGroup(final Bases bases) 
      {
        this.bases = bases;
    
        addFeatureChangeListener(getActionController());
        addEntryChangeListener(getActionController());
        getBases().addSequenceChangeListener(getActionController(),
                                             Bases.MIN_PRIORITY);
        addEntryGroupChangeListener(getActionController());
      }
    
      public SimpleEntryGroup() 
      {
        addFeatureChangeListener(getActionController());
        addEntryGroupChangeListener(getActionController());
      }
      
      /**
       *  Returns true if and only if there are any unsaved changes in any of the
       *  Entry objects in this EntryGroup.
       **/
      public boolean hasUnsavedChanges() 
      {
        final int my_size = size();
        for(int entry_index = 0; entry_index < my_size;
            ++entry_index) 
        {
          if(elementAt(entry_index).hasUnsavedChanges()) 
            return true;
        }
    
        return false;
      }
    
      /**
       *  Return the default Entry for this SimpleEntryGroup.  The "default" is the
       *  Entry where new features are created.
       **/
      public Entry getDefaultEntry() 
      {
        return default_entry;
      }
    
      /**
       *  Set the default Entry.  The "default" is the Entry where new features
       *  are created.
       *  @param entry The new default entry.  If this Entry is not active this
       *    method will return immediately.
       **/
      public void setDefaultEntry(Entry entry) 
      {
        if(entry != null && !isActive(entry))
          return;
    
        // do nothing
        if(default_entry == entry) 
          return;
        else 
          default_entry = entry;
    
        // now inform the listeners that a change has occured
        final EntryGroupChangeEvent event =
          new EntryGroupChangeEvent(this, getDefaultEntry(),
                                    EntryGroupChangeEvent.NEW_DEFAULT_ENTRY);
    
        fireEvent(entry_group_listener_list, event);
      }
    
      /**
       *  Return the index of a feature within this object.  This method treats
       *  all the features in all the active entries as if they were in one big
       *  array.  The first feature of the first entry will have index 1, the
       *  first from the second entry will have index 1 +(the number of features
       *  in the first entry), etc.
       *  @param feature The feature to find the index of.
       *  @return The index of the feature or -1 if the feature isn't in any of
       *    the entries.  The first index is 0 the last is the total number of
       *    features in all the entries of this object minus one.
       **/
      public int indexOf(Feature feature) 
      {
        int feature_count_of_previous_entries = 0;
        final int active_entries_size = active_entries.size();
    
        for(int entry_index = 0; entry_index < active_entries_size;
            ++entry_index) 
        {
          final Entry this_entry  = active_entries.elementAt(entry_index);
          final int feature_index = this_entry.indexOf(feature);
    
          if(feature_index != -1) 
            return feature_index + feature_count_of_previous_entries;
    
          feature_count_of_previous_entries += this_entry.getFeatureCount();
        }
    
        return -1;
      }
    
      /**
       *  Return true if any of the active entries in the group contains the given
       *  feature.
       **/
      public boolean contains(Feature feature) 
      {
        final int active_entries_size = active_entries.size();
    
        for(int i = 0; i < active_entries_size; ++i) 
        {
          final Entry current_entry = active_entries.elementAt(i);
    
          if(current_entry.contains(feature)) 
            return true;
        }
    
        return false;
      }
    
      /**
       *  Return true if the given Entry is active(visible).  The Feature objects
       *  in an Entry that is not active will be ignored by the methods that deal
       *  will features: featureAt(), indexOf(), contains(), features(), etc.
       **/
      public boolean isActive(Entry entry) 
      {
        if(active_entries.contains(entry)) 
          return true;
        else 
          return false;
      }
    
      /**
       *  Set the "active" setting of the Entry at the given index.  If the index
       *  refers to the default entry and new_active is false, the default entry
       *  will be set to the active entry or null if there are no active entries.
       *  @param index The index of the Entry to change.
       *  @param active The new active setting.
       **/
      public void setIsActive(int index, boolean new_active) 
      {
        final Entry entry = elementAt(index);
    
        if(new_active)
        {
          // no change
          if(isActive(entry)) 
            return;
          else 
          {
            // this is slow but it guarantees that the Entry references in the
            // active_entries vector are in the same order as in the
            // SimpleEntryGroup
    
            final EntryVector new_active_entries = new EntryVector();
            final int my_size = size();
    
            for(int i = 0; i < my_size; ++i) 
            {
              if(active_entries.contains(elementAt(i)) || index == i) 
                new_active_entries.add(elementAt(i));
            }
    
            active_entries.removeAllElements();
    
            final int new_active_entries_size = new_active_entries.size();
    
            for(int i = 0; i < new_active_entries_size; ++i) 
              active_entries.add(new_active_entries.elementAt(i));
    
            if(active_entries.size() >= 1 && getDefaultEntry() == null) 
            {
              // there was no default entry before calling addElement() so
              // make the first non-sequence entry the default entry
              if(active_entries.elementAt(0) == getSequenceEntry() &&
                 active_entries.size() == 1)
              {
                // don't set the default entry to be the sequence entry unless
                // the user asks for it
                setDefaultEntry(null);
              }
              else 
              {
                if(active_entries.size() == 1) 
                  setDefaultEntry(active_entries.elementAt(0));
                else 
                  setDefaultEntry(active_entries.elementAt(1));
              }
            }
          }
        }
        else 
        {
          // no change
          if(!isActive(entry)) 
            return;
          else
          {
            active_entries.removeElement(entry);
    
            if(entry == getDefaultEntry()) 
            {
              if(active_entries.size() > 0) 
              {
                if(active_entries.elementAt(0) == getSequenceEntry()) 
                {
                  // don't set the default entry to be the sequence entry unless
                  // the user asks for it
                  if(active_entries.size() > 1)
                    setDefaultEntry(active_entries.elementAt(1));
                  else
                    setDefaultEntry(null);
                }
                else 
                  setDefaultEntry(active_entries.elementAt(0));
              }
              else 
                setDefaultEntry(null);
            }
          }
        }
    
        // now inform the listeners that a change has occured
        final EntryGroupChangeEvent event;
    
        // change state
        if(new_active)        // become active
          event = new EntryGroupChangeEvent(this, entry,
                                            EntryGroupChangeEvent.ENTRY_ACTIVE);
        else                  // become inactive
          event = new EntryGroupChangeEvent(this, entry,
                                            EntryGroupChangeEvent.ENTRY_INACTIVE);
    
        fireEvent(entry_group_listener_list, event);
      }
    
      /**
       *  Set the "active" setting of the given Entry.  The Entry is the default
       *  entry and new_active is false, the default entry will be set to the
       *  active entry or null if there are no active entries.
       *  @param entry The Entry to activate or deactivate.
       *  @param new_active The new active setting.
       **/
      public void setIsActive(final Entry entry, final boolean new_active) 
      {
        setIsActive(indexOf(entry), new_active);
      }
    
    
      /**
       *  Return the Entry from this SimpleEntryGroup that contains the sequence
       *  to view or return null if none of the entries contains a sequence.
       **/
      public Entry getSequenceEntry() 
      {
        if(size() == 0) 
          return null;
        else 
          return elementAt(0);
      }
    
      /**
       *  Returns the base length of the sequence of the first Entry in this group
       *  or 0 if this group is empty.
       **/
      public int getSequenceLength() 
      {
        return getBases().getLength();
      }
    
      /**
       *  Returns the Bases object of the first Entry in this group or null if
       *  this group is empty.
       **/
      public Bases getBases() 
      {
        return bases;
      }
    
      /**
       *  Reverse and complement the sequence and all features in every Entry in
       *  this SimpleEntryGroup.
       **/
      public void reverseComplement()
          throws ReadOnlyException 
      {
        if(isReadOnly()) 
          throw new ReadOnlyException();
    
        // reverse the sequence
        getBases().reverseComplement();
      }
    
      /**
       *  Return true if and only if one or more of the entries or features in
       *  this SimpleEntryGroup are read-only.
       **/
      public boolean isReadOnly() 
      {
        final int my_size = size();
        for(int i = 0; i < my_size; ++i) 
        {
          final Entry this_entry = elementAt(i);
    
          if(this_entry.isReadOnly()) 
            return true;
    
          final FeatureEnumeration feature_enum = this_entry.features();
    
          while(feature_enum.hasMoreFeatures()) 
          {
            if(feature_enum.nextFeature().isReadOnly()) 
              return true;
          }
        }
    
        return false;
      }
    
      /**
       *  Increment the reference count for this EntryGroup.
       **/
      public void ref() 
      {
        ++reference_count;
      }
    
      /**
       *  Decrement the reference count for this EntryGroup.  When the reference
       *  count goes to zero a EntryGroupChangeEvent is sent to all
       *  EntryGroupChangeListeners with type EntryGroupChangeEvent.DONE_GONE.
       *  The listeners should then stop using the EntryGroup and release any
       *  associated resources.
       **/
      public void unref() 
      {
        --reference_count;
    
        if(reference_count == 0) 
        {
          // remove all the entries which will close any edit or view windows
          while(size() > 0) 
          {
            final Entry this_entry = elementAt(0);
            remove(this_entry);
            
            this_entry.getEMBLEntry().dispose();
          }
    
          // now inform the listeners that the EntryGroup is no more
          final EntryGroupChangeEvent event =
                  new EntryGroupChangeEvent(this, null,
                                            EntryGroupChangeEvent.DONE_GONE);
    
          fireEvent(entry_group_listener_list, event);
        }
      }
    
      /**
       *  Return the current reference count.
       **/
      public int refCount() 
      {
        return reference_count;
      }
    
      /**
       *  Return the Feature at the given index.  This method treats all the
       *  features in all the entries as if they were in one big array.  See
       *  the comment on indexOf().
       *  @param index The index of the required Feature.
       *  @return The Feature at the given index.  The first index is 0 the last
       *    is the total number of features in all the entries of this object minus
       *    one.  If the index is out of range then null will be returned.
       **/
      public Feature featureAt(int index) 
      {
        if(index < 0) 
          throw new Error("internal error - index out of range: " + index);
    
        final int active_entries_size = active_entries.size();
    
        for(int entry_index = 0; entry_index < active_entries_size; 
            ++entry_index)
        {
          final Entry this_entry = active_entries.elementAt(entry_index);
    
          if(index < this_entry.getFeatureCount()) 
            return this_entry.getFeature(index);
    
          index -= this_entry.getFeatureCount();
        }
    
        throw new Error("internal error - index out of range: " + index);
      }
    
      /**
       *  Return a vector containing the references of the Feature objects within
       *  the given range of indices.
       *  @param start_index The index of the first feature to return.
       *  @param end_index The index of the last feature to return.
       **/
      public FeatureVector getFeaturesInIndexRange(final int start_index,
                                                   final int end_index) 
      {
        final FeatureVector return_vector = new FeatureVector();
    
        for(int i = start_index; i <= end_index; ++i) 
          return_vector.add(featureAt(i));
    
        return return_vector;
      }
    
      /**
       *  Return a vector containing the references of the Feature objects within
       *  the given range for all the active entries in the SimpleEntryGroup.
       *  @param range Return features that overlap this range - ie the start of
       *    the feature is less than or equal to the end of the range and the end
       *    of the feature is greater than or equal to the start of the range.
       *  @return The non-source key features of this feature table the are within
       *    the given range.  The returned object is a copy - changes will not
       *    effect the FeatureTable object itself.
       **/
      public FeatureVector getFeaturesInRange(Range range)
          throws OutOfRangeException 
      {
        final FeatureVector return_vector = new FeatureVector();
        final int my_size = size();
    
        for(int i = 0; i < my_size; ++i) 
        {
          final Entry this_entry = elementAt(i);
    
          if(isActive(this_entry)) 
          {
            final FeatureVector visible_entry_features =
              elementAt(i).getFeaturesInRange(range);
    
            final int visible_entry_features_size = visible_entry_features.size();
    
            for(int feature_index = 0; feature_index < visible_entry_features_size;
                ++feature_index) 
            {
              final Feature this_feature =
                visible_entry_features.elementAt(feature_index);
              return_vector.add(this_feature);
            }
          }
        }
    
        return return_vector;
      }
    
      /**
       *  Return a vector containing the references of the Feature objects from
       *  all the active entries in the SimpleEntryGroup.
       *  @return The non-source key features in active entries of this
       *    SimpleEntryGroup.  The returned object is a copy - changes will not
       *    effect the SimpleEntryGroup object itself.
       **/
      public FeatureVector getAllFeatures() 
      {
        final FeatureVector return_vector = new FeatureVector();
        final int my_size = size();
    
        for(int i = 0; i < my_size; ++i) 
        {
          final Entry this_entry = elementAt(i);
    
          if(isActive(this_entry)) 
          {
            final FeatureVector entry_features = elementAt(i).getAllFeatures();
            final int entry_features_size = entry_features.size();
    
            for(int feature_index = 0; feature_index < entry_features_size;
                ++feature_index) 
            {
              final Feature this_feature =
                entry_features.elementAt(feature_index);
              return_vector.add(this_feature);
            }
          }
        }
    
        return return_vector;
      }
    
      /**
       *  Return a count of the number of Feature objects from all the active
       *  entries in the SimpleEntryGroup.
       *  @return A count of the non-source key features in active entries of this
       *    SimpleEntryGroup.
       **/
      public int getAllFeaturesCount() 
      {
        int return_count = 0;
        final int my_size = size();
    
        for(int i = 0; i < my_size; ++i) 
        {
          final Entry this_entry = elementAt(i);
    
          if(isActive(this_entry)) 
            return_count += this_entry.getFeatureCount();
        }
    
        return return_count;
      }
    
      /**
       *  Add an Entry to this object and then emit the appropriate EntryChange
       *  events.
       **/
      public void addElement(Entry entry) 
      {
        super.addElement(entry);
    
        // set the default Entry to whichever Entry gets added first
        if(default_entry == null) 
          default_entry = entry;
    
        active_entries.add(entry);
    
        // now inform the listeners that an addition has occured
        final EntryGroupChangeEvent event =
          new EntryGroupChangeEvent(this, entry,
                                    EntryGroupChangeEvent.ENTRY_ADDED);
    
        fireEvent(entry_group_listener_list, event);
    
        // make the new entry the default entry if and only if there was no entry
        // previously or there was only one entry previously and it contained
        // sequence but no features
        if(size() == 1) 
          setDefaultEntry(entry);
        else 
        {
          if(size() == 2) 
          {
            final Entry first_entry = elementAt(0);
    
            if(first_entry.getFeatureCount() == 0) 
            {
              final Bases first_entry_bases = first_entry.getBases();
    
              if(first_entry_bases != null &&
                 first_entry_bases.getLength() > 0) 
                setDefaultEntry(entry);
            }
          }
        }
    
        entry.addEntryChangeListener(this);
        entry.addFeatureChangeListener(this);
      }
    
      /**
       *  A convenience method that does the same as addElement(Entry).
       **/
      public void add(final Entry entry) 
      {
        if(entry.getEMBLEntry() instanceof IndexedGFFDocumentEntry)
          ((IndexedGFFDocumentEntry)entry.getEMBLEntry()).setEntryGroup(this);
        else if(entry.getEMBLEntry() instanceof GFFDocumentEntry)
        {
          ((GFFDocumentEntry)entry.getEMBLEntry()).adjustCoordinates( getSequenceEntry() );
          if(!Options.isBlackBeltMode() && size() > 1 && 
              entry.getEMBLEntry().getSequence() != null )
          {
            new MessageDialog (null, "Warning", 
                "Overlaying a GFF with a sequence onto an entry with a sequence.",
                false);
          }
        }
    
        addElement(entry);
      }
    
      /**
       *  Remove an Entry from this object and then emit the appropriate
       *  EntryGroupChange events.  The first entry in the group can only be
       *  removed if it is the only Entry because the first Entry contains the
       *  sequence.
       *  @return true if the removal succeeded, false if it fails(which can if
       *    the given Entry isn't in this SimpleEntryGroup or if the user tries to
       *    remove the first Entry).
       **/
      public boolean removeElement(final Entry entry) 
      {
        // this call will sort out the default entry
        setIsActive(indexOf(entry), false);
    
        entry.dispose();
    
        final boolean remove_return = super.removeElement(entry);
    
        entry.removeEntryChangeListener(this);
        entry.removeFeatureChangeListener(this);
    
        active_entries.removeElement(entry);
    
        // now inform the listeners that a deletion has occured
        final EntryGroupChangeEvent event =
          new EntryGroupChangeEvent(this, entry,
                                    EntryGroupChangeEvent.ENTRY_DELETED);
    
        fireEvent(entry_group_listener_list, event);
    
        return remove_return;
      }
    
      /**
       *  A convenience method that does the same as removeElement(Entry).
       **/
      public boolean remove(final Entry entry) 
      {
        return removeElement(entry);
      }
    
      /**
       *  Create a new(blank) Feature in the default Entry of this
       *  SimpleEntryGroup.  See getDefaultEntry() and Entry.createFeature().
       *  @return The new Feature.
       **/
      public Feature createFeature() throws ReadOnlyException
      {
        final Feature new_feature = getDefaultEntry().createFeature();
        return new_feature;
      }
    
      /**
       *  Create a new(empty) Entry in this SimpleEntryGroup.  See
       *  Entry.newEntry().
       *  @return The reference of the new Entry.
       **/
      public Entry createEntry() 
      {
        Entry new_entry = null;
        Entry default_entry = getDefaultEntry();
        if(default_entry != null &&
           default_entry.getEMBLEntry() != null &&
           default_entry.getEMBLEntry() instanceof DatabaseDocumentEntry)
        {
          DatabaseDocument doc =
            (DatabaseDocument)((DocumentEntry)default_entry.getEMBLEntry()).getDocument();
          DatabaseDocument new_doc = doc.createDatabaseDocument();
          
          try
          {
            DatabaseDocumentEntry new_doc_entry = 
              new DatabaseDocumentEntry();
            new_doc_entry.setDocument(new_doc);
            new_entry = new Entry(getBases(), new_doc_entry);
          }
          catch(Exception e)
          {
            e.printStackTrace();
          }
          
        }
        else
          new_entry = Entry.newEntry(getBases());
        
        add(new_entry);
        return new_entry;
      }
    
      /**
       *  Create a new(empty) Entry in this SimpleEntryGroup.  See
       *  Entry.newEntry().
       *  @param name The(file) name of the new Entry.
       *  @return The reference of the new Entry.
       **/
      public Entry createEntry(final String name) 
      {
        final Entry new_entry = createEntry();
        new_entry.setName(name);
        return new_entry;
      }
    
      /**
       *  Returns an enumeration of the Feature objects in this
       *  SimpleEntryGroup. The returned FeatureEnumeration object will generate
       *  all features in this object in turn. The first item generated is the
       *  item at index 0, then the item at index 1, and so on.
       **/
      public FeatureEnumeration features() 
      {
        return new FeatureEnumerator();
      }
    
      /**
       *  An Enumeration of Feature objects.
       **/
      public class FeatureEnumerator implements FeatureEnumeration 
      {
    
        /**
         *  The EntryVector object that we are enumerating.  Set to null when there
         *  are no more Feature objects.
         **/
        private EntryVector active_entries;
    
        /**
         *  The index of the Entry that we will get the next Feature from.
         **/
        private int entry_index = -1;
    
        /** Enumeration for the current entry */
        private FeatureEnumeration feature_enumerator;
    
        /**
         *  Create a new FeatureEnumeration that will enumerate the enclosing
         *  SimpleEntryGroup object.  The SimpleEntryGroup object must not be
         *  changed while the enumeration is active.
         **/
        public FeatureEnumerator() 
        {
          active_entries = getActiveEntries();
    
          entry_index = 0;
    
          if(active_entries.size() > 0) 
            feature_enumerator =
              active_entries.elementAt(entry_index).features();
          else 
            feature_enumerator = null;
        }
    
        /**
         *  See the FeatureEnumeration interface for details.
         **/
        public boolean hasMoreFeatures() 
        {
          if(feature_enumerator == null) 
            return false;
    
          if(feature_enumerator.hasMoreFeatures()) 
            return true;
    
          ++entry_index;
    
          if(entry_index == active_entries.size()) 
            return false;
          else
          {
            feature_enumerator =
              active_entries.elementAt(entry_index).features();
            return hasMoreFeatures();
          }
        }
    
        /**
         *  See the FeatureEnumeration interface for details.
         **/
        public Feature nextFeature()
            throws NoSuchElementException 
        {
          if(feature_enumerator == null) 
            throw new NoSuchElementException();
    
          return feature_enumerator.nextFeature();
        }
    
      }
    
      /**
       *  Implementation of the FeatureChangeListener interface.  We listen for
       *  changes in every feature of every entry in this group.
       **/
      public void featureChanged(FeatureChangeEvent event) 
      {
        // pass the action straight through
        fireEvent(feature_listener_list, event);
      }
    
      /**
       *  Implementation of the EntryChangeListener interface.  We listen for
       *  changes from every entry in this group and pass the events though to all
       *  the object listening for EntryChangeEvents for the event from this
       *  SimpleEntryGroup.
       **/
      public void entryChanged(EntryChangeEvent event) 
      {
        // pass the action straight through
        fireEvent(entry_listener_list, event);
      }
    
      /**
       *  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 fireEvent(Vector listeners, ChangeEvent event) 
      {
        final Vector targets;
        // copied from a book - synchronising the whole method might cause a
        // deadlock
        synchronized(this) 
        {
          targets = (Vector)listeners.clone();
        }
    
        //boolean seen_chado_manager = false;
        final int targets_size = targets.size();
        for(int i = 0; i < targets_size; ++i) 
        {
          ChangeListener target =(ChangeListener) targets.elementAt(i);
    
          if(event instanceof EntryGroupChangeEvent)
          {
            final EntryGroupChangeListener entry_group_change_listener =
             (EntryGroupChangeListener) target;
            final EntryGroupChangeEvent group_change_event =
             (EntryGroupChangeEvent) event;
            entry_group_change_listener.entryGroupChanged(group_change_event);
          } 
          else
          {
            if(event instanceof EntryChangeEvent) 
            {
              final EntryChangeListener entry_change_listener =
                                         (EntryChangeListener) target;
    
    //        if(entry_change_listener instanceof ChadoTransactionManager)
    //        {
                // just call this listener once
    //          if(!seen_chado_manager)
    //          {
    //            entry_change_listener.entryChanged((EntryChangeEvent) event);
    //            seen_chado_manager = true;
    //          }
    //        }
    //        else  
                entry_change_listener.entryChanged((EntryChangeEvent) event);
            } 
            else 
            {
              final FeatureChangeListener feature_change_listener =
                                        (FeatureChangeListener) target;
              feature_change_listener.featureChanged((FeatureChangeEvent) event);
            }
    
          }
        }
      }
    
      /**
       *  Adds the specified event listener to receive entry group change events
       *  from this object.
       *  @param l the event change listener.
       **/
      public void addEntryGroupChangeListener(EntryGroupChangeListener l) 
      {
        entry_group_listener_list.addElement(l);
      }
    
      /**
       *  Removes the specified event listener so that it no longer receives
       *  entry group change events from this object.
       *  @param l the event change listener.
       **/
      public void removeEntryGroupChangeListener(EntryGroupChangeListener l) 
      {
        entry_group_listener_list.removeElement(l);
      }
    
      /**
       *  Adds the specified event listener to receive entry change events from
       *  this object.
       *  @param l the event change listener.
       **/
      public void addEntryChangeListener(EntryChangeListener l) 
      {
        entry_listener_list.addElement(l);
      }
    
      /**
       *  Removes the specified event listener so that it no longer receives
       *  entry change events from this object.
       *  @param l the event change listener.
       **/
      public void removeEntryChangeListener(EntryChangeListener l) 
      {
        entry_listener_list.removeElement(l);
      }
    
      /**
       *  Adds the specified event listener to receive feature change events from
       *  this object.
       *  @param l the event change listener.
       **/
      public void addFeatureChangeListener(FeatureChangeListener l) 
      {
        feature_listener_list.addElement(l);
      }
    
      /**
       *  Removes the specified event listener so that it no longer receives
       *  feature change events from this object.
       *  @param l the event change listener.
       **/
      public void removeFeatureChangeListener(FeatureChangeListener l) 
      {
        feature_listener_list.removeElement(l);
      }
    
      /**
       *  Return the reference of an EntryVector containing the active entries of
       *  this EntryGroup.
       **/
      public EntryVector getActiveEntries() 
      {
        return(EntryVector) active_entries.clone();
      }
    
      /**
       *  This method translates the start and end of every each Range in every
       *  Location into another coordinate system.  The Ranges will be truncated
       *  if necessary.
       *  @param constraint This contains the start and end base of the new
       *    coordinate system.  The position given by constraint.getStart() will
       *    be at postion/base 1 in the new coordinate system.
       *  @return a copy of the EntryGroup which has been translated into the new
       *    coordinate system.
       **/
      public EntryGroup truncate(final Range constraint) 
      {
        final Bases new_bases = getBases().truncate(constraint);
    
        final EntryGroup new_entry_group = new SimpleEntryGroup(new_bases);
        final int my_size = size();
    
        for(int i = 0; i < my_size; ++i) 
        {
          final Entry this_entry = elementAt(i);
          final Entry new_entry = this_entry.truncate(new_bases, constraint);
          new_entry_group.add(new_entry);
        }
    
        if(size() > 0) 
        {
          final StreamSequence sequence =
           (StreamSequence) new_bases.getSequence();
          final SimpleDocumentEntry document_entry =
           (SimpleDocumentEntry) new_entry_group.elementAt(0).getEMBLEntry();
          document_entry.setSequence(sequence);
        }
    
        return new_entry_group;
      }
    
      /**
       *  Return the ActionController for this EntryGroup(for undo).
       **/
      public ActionController getActionController() 
      {
        return action_controller;
      }
      
    }