1 /* SimpleEntryGroup.java 2 * 3 * created: Wed Nov 11 1998 4 * 5 * This file is part of Artemis 6 * 7 * Copyright(C) 1998,1999,2000,2001,2002 Genome Research Limited 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; either version 2 12 * of the License, or(at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 * 23 * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/SimpleEntryGroup.java,v 1.8 2008-06-11 15:15:23 tjc Exp $ 24 **/ 25 26 package uk.ac.sanger.artemis; 27 28 import uk.ac.sanger.artemis.sequence.*; 29 import uk.ac.sanger.artemis.components.MessageDialog; 30 import uk.ac.sanger.artemis.io.GFFDocumentEntry; 31 import uk.ac.sanger.artemis.io.IndexedGFFDocumentEntry; 32 import uk.ac.sanger.artemis.io.Range; 33 import uk.ac.sanger.artemis.io.StreamSequence; 34 import uk.ac.sanger.artemis.io.SimpleDocumentEntry; 35 import uk.ac.sanger.artemis.io.DatabaseDocumentEntry; 36 import uk.ac.sanger.artemis.io.DocumentEntry; 37 import uk.ac.sanger.artemis.util.DatabaseDocument; 38 import uk.ac.sanger.artemis.util.ReadOnlyException; 39 import uk.ac.sanger.artemis.util.OutOfRangeException; 40 41 import java.util.Vector; 42 import java.util.NoSuchElementException; 43 44 /** 45 * This class implements a vector of Entry objects, with additional methods 46 * for querying and changing the feature tables of all the entries at 47 * once. Objects of this class act a bit like single Entry objects. 48 * 49 * @author Kim Rutherford 50 * @version $Id: SimpleEntryGroup.java,v 1.8 2008-06-11 15:15:23 tjc Exp $ 51 **/ 52 53 public class SimpleEntryGroup extends EntryVector 54 implements EntryGroup 55 { 56 57 /** vector of those objects listening for entry change events. */ 58 final private Vector entry_group_listener_list = new Vector(); 59 60 /** vector of those objects listening for entry change events. */ 61 final private Vector entry_listener_list = new Vector(); 62 63 /** vector of those objects listening for feature change events. */ 64 final private Vector feature_listener_list = new Vector(); 65 66 /** vector of entries that are currently active (visible). */ 67 final private EntryVector active_entries = new EntryVector(); 68 69 /** 70 * The default Entry for this SimpleEntryGroup. The "default" is the Entry 71 * where new features are created. 72 **/ 73 private Entry default_entry = null; 74 75 /** Bases object that was passed to the constructor. */ 76 private Bases bases; 77 78 /** Incremented by ref(), decremented by unref(). */ 79 private int reference_count = 0; 80 81 /** The ActionController of this EntryGroup (used for undo). */ 82 final private ActionController action_controller = new ActionController(); 83 84 /** 85 * Create a new empty SimpleEntryGroup object. 86 **/ SimpleEntryGroup(final Bases bases)87 public SimpleEntryGroup(final Bases bases) 88 { 89 this.bases = bases; 90 91 addFeatureChangeListener(getActionController()); 92 addEntryChangeListener(getActionController()); 93 getBases().addSequenceChangeListener(getActionController(), 94 Bases.MIN_PRIORITY); 95 addEntryGroupChangeListener(getActionController()); 96 } 97 SimpleEntryGroup()98 public SimpleEntryGroup() 99 { 100 addFeatureChangeListener(getActionController()); 101 addEntryGroupChangeListener(getActionController()); 102 } 103 104 /** 105 * Returns true if and only if there are any unsaved changes in any of the 106 * Entry objects in this EntryGroup. 107 **/ hasUnsavedChanges()108 public boolean hasUnsavedChanges() 109 { 110 final int my_size = size(); 111 for(int entry_index = 0; entry_index < my_size; 112 ++entry_index) 113 { 114 if(elementAt(entry_index).hasUnsavedChanges()) 115 return true; 116 } 117 118 return false; 119 } 120 121 /** 122 * Return the default Entry for this SimpleEntryGroup. The "default" is the 123 * Entry where new features are created. 124 **/ getDefaultEntry()125 public Entry getDefaultEntry() 126 { 127 return default_entry; 128 } 129 130 /** 131 * Set the default Entry. The "default" is the Entry where new features 132 * are created. 133 * @param entry The new default entry. If this Entry is not active this 134 * method will return immediately. 135 **/ setDefaultEntry(Entry entry)136 public void setDefaultEntry(Entry entry) 137 { 138 if(entry != null && !isActive(entry)) 139 return; 140 141 // do nothing 142 if(default_entry == entry) 143 return; 144 else 145 default_entry = entry; 146 147 // now inform the listeners that a change has occured 148 final EntryGroupChangeEvent event = 149 new EntryGroupChangeEvent(this, getDefaultEntry(), 150 EntryGroupChangeEvent.NEW_DEFAULT_ENTRY); 151 152 fireEvent(entry_group_listener_list, event); 153 } 154 155 /** 156 * Return the index of a feature within this object. This method treats 157 * all the features in all the active entries as if they were in one big 158 * array. The first feature of the first entry will have index 1, the 159 * first from the second entry will have index 1 +(the number of features 160 * in the first entry), etc. 161 * @param feature The feature to find the index of. 162 * @return The index of the feature or -1 if the feature isn't in any of 163 * the entries. The first index is 0 the last is the total number of 164 * features in all the entries of this object minus one. 165 **/ indexOf(Feature feature)166 public int indexOf(Feature feature) 167 { 168 int feature_count_of_previous_entries = 0; 169 final int active_entries_size = active_entries.size(); 170 171 for(int entry_index = 0; entry_index < active_entries_size; 172 ++entry_index) 173 { 174 final Entry this_entry = active_entries.elementAt(entry_index); 175 final int feature_index = this_entry.indexOf(feature); 176 177 if(feature_index != -1) 178 return feature_index + feature_count_of_previous_entries; 179 180 feature_count_of_previous_entries += this_entry.getFeatureCount(); 181 } 182 183 return -1; 184 } 185 186 /** 187 * Return true if any of the active entries in the group contains the given 188 * feature. 189 **/ contains(Feature feature)190 public boolean contains(Feature feature) 191 { 192 final int active_entries_size = active_entries.size(); 193 194 for(int i = 0; i < active_entries_size; ++i) 195 { 196 final Entry current_entry = active_entries.elementAt(i); 197 198 if(current_entry.contains(feature)) 199 return true; 200 } 201 202 return false; 203 } 204 205 /** 206 * Return true if the given Entry is active(visible). The Feature objects 207 * in an Entry that is not active will be ignored by the methods that deal 208 * will features: featureAt(), indexOf(), contains(), features(), etc. 209 **/ isActive(Entry entry)210 public boolean isActive(Entry entry) 211 { 212 if(active_entries.contains(entry)) 213 return true; 214 else 215 return false; 216 } 217 218 /** 219 * Set the "active" setting of the Entry at the given index. If the index 220 * refers to the default entry and new_active is false, the default entry 221 * will be set to the active entry or null if there are no active entries. 222 * @param index The index of the Entry to change. 223 * @param active The new active setting. 224 **/ setIsActive(int index, boolean new_active)225 public void setIsActive(int index, boolean new_active) 226 { 227 final Entry entry = elementAt(index); 228 229 if(new_active) 230 { 231 // no change 232 if(isActive(entry)) 233 return; 234 else 235 { 236 // this is slow but it guarantees that the Entry references in the 237 // active_entries vector are in the same order as in the 238 // SimpleEntryGroup 239 240 final EntryVector new_active_entries = new EntryVector(); 241 final int my_size = size(); 242 243 for(int i = 0; i < my_size; ++i) 244 { 245 if(active_entries.contains(elementAt(i)) || index == i) 246 new_active_entries.add(elementAt(i)); 247 } 248 249 active_entries.removeAllElements(); 250 251 final int new_active_entries_size = new_active_entries.size(); 252 253 for(int i = 0; i < new_active_entries_size; ++i) 254 active_entries.add(new_active_entries.elementAt(i)); 255 256 if(active_entries.size() >= 1 && getDefaultEntry() == null) 257 { 258 // there was no default entry before calling addElement() so 259 // make the first non-sequence entry the default entry 260 if(active_entries.elementAt(0) == getSequenceEntry() && 261 active_entries.size() == 1) 262 { 263 // don't set the default entry to be the sequence entry unless 264 // the user asks for it 265 setDefaultEntry(null); 266 } 267 else 268 { 269 if(active_entries.size() == 1) 270 setDefaultEntry(active_entries.elementAt(0)); 271 else 272 setDefaultEntry(active_entries.elementAt(1)); 273 } 274 } 275 } 276 } 277 else 278 { 279 // no change 280 if(!isActive(entry)) 281 return; 282 else 283 { 284 active_entries.removeElement(entry); 285 286 if(entry == getDefaultEntry()) 287 { 288 if(active_entries.size() > 0) 289 { 290 if(active_entries.elementAt(0) == getSequenceEntry()) 291 { 292 // don't set the default entry to be the sequence entry unless 293 // the user asks for it 294 if(active_entries.size() > 1) 295 setDefaultEntry(active_entries.elementAt(1)); 296 else 297 setDefaultEntry(null); 298 } 299 else 300 setDefaultEntry(active_entries.elementAt(0)); 301 } 302 else 303 setDefaultEntry(null); 304 } 305 } 306 } 307 308 // now inform the listeners that a change has occured 309 final EntryGroupChangeEvent event; 310 311 // change state 312 if(new_active) // become active 313 event = new EntryGroupChangeEvent(this, entry, 314 EntryGroupChangeEvent.ENTRY_ACTIVE); 315 else // become inactive 316 event = new EntryGroupChangeEvent(this, entry, 317 EntryGroupChangeEvent.ENTRY_INACTIVE); 318 319 fireEvent(entry_group_listener_list, event); 320 } 321 322 /** 323 * Set the "active" setting of the given Entry. The Entry is the default 324 * entry and new_active is false, the default entry will be set to the 325 * active entry or null if there are no active entries. 326 * @param entry The Entry to activate or deactivate. 327 * @param new_active The new active setting. 328 **/ setIsActive(final Entry entry, final boolean new_active)329 public void setIsActive(final Entry entry, final boolean new_active) 330 { 331 setIsActive(indexOf(entry), new_active); 332 } 333 334 335 /** 336 * Return the Entry from this SimpleEntryGroup that contains the sequence 337 * to view or return null if none of the entries contains a sequence. 338 **/ getSequenceEntry()339 public Entry getSequenceEntry() 340 { 341 if(size() == 0) 342 return null; 343 else 344 return elementAt(0); 345 } 346 347 /** 348 * Returns the base length of the sequence of the first Entry in this group 349 * or 0 if this group is empty. 350 **/ getSequenceLength()351 public int getSequenceLength() 352 { 353 return getBases().getLength(); 354 } 355 356 /** 357 * Returns the Bases object of the first Entry in this group or null if 358 * this group is empty. 359 **/ getBases()360 public Bases getBases() 361 { 362 return bases; 363 } 364 365 /** 366 * Reverse and complement the sequence and all features in every Entry in 367 * this SimpleEntryGroup. 368 **/ reverseComplement()369 public void reverseComplement() 370 throws ReadOnlyException 371 { 372 if(isReadOnly()) 373 throw new ReadOnlyException(); 374 375 // reverse the sequence 376 getBases().reverseComplement(); 377 } 378 379 /** 380 * Return true if and only if one or more of the entries or features in 381 * this SimpleEntryGroup are read-only. 382 **/ isReadOnly()383 public boolean isReadOnly() 384 { 385 final int my_size = size(); 386 for(int i = 0; i < my_size; ++i) 387 { 388 final Entry this_entry = elementAt(i); 389 390 if(this_entry.isReadOnly()) 391 return true; 392 393 final FeatureEnumeration feature_enum = this_entry.features(); 394 395 while(feature_enum.hasMoreFeatures()) 396 { 397 if(feature_enum.nextFeature().isReadOnly()) 398 return true; 399 } 400 } 401 402 return false; 403 } 404 405 /** 406 * Increment the reference count for this EntryGroup. 407 **/ ref()408 public void ref() 409 { 410 ++reference_count; 411 } 412 413 /** 414 * Decrement the reference count for this EntryGroup. When the reference 415 * count goes to zero a EntryGroupChangeEvent is sent to all 416 * EntryGroupChangeListeners with type EntryGroupChangeEvent.DONE_GONE. 417 * The listeners should then stop using the EntryGroup and release any 418 * associated resources. 419 **/ unref()420 public void unref() 421 { 422 --reference_count; 423 424 if(reference_count == 0) 425 { 426 // remove all the entries which will close any edit or view windows 427 while(size() > 0) 428 { 429 final Entry this_entry = elementAt(0); 430 remove(this_entry); 431 432 this_entry.getEMBLEntry().dispose(); 433 } 434 435 // now inform the listeners that the EntryGroup is no more 436 final EntryGroupChangeEvent event = 437 new EntryGroupChangeEvent(this, null, 438 EntryGroupChangeEvent.DONE_GONE); 439 440 fireEvent(entry_group_listener_list, event); 441 } 442 } 443 444 /** 445 * Return the current reference count. 446 **/ refCount()447 public int refCount() 448 { 449 return reference_count; 450 } 451 452 /** 453 * Return the Feature at the given index. This method treats all the 454 * features in all the entries as if they were in one big array. See 455 * the comment on indexOf(). 456 * @param index The index of the required Feature. 457 * @return The Feature at the given index. The first index is 0 the last 458 * is the total number of features in all the entries of this object minus 459 * one. If the index is out of range then null will be returned. 460 **/ featureAt(int index)461 public Feature featureAt(int index) 462 { 463 if(index < 0) 464 throw new Error("internal error - index out of range: " + index); 465 466 final int active_entries_size = active_entries.size(); 467 468 for(int entry_index = 0; entry_index < active_entries_size; 469 ++entry_index) 470 { 471 final Entry this_entry = active_entries.elementAt(entry_index); 472 473 if(index < this_entry.getFeatureCount()) 474 return this_entry.getFeature(index); 475 476 index -= this_entry.getFeatureCount(); 477 } 478 479 throw new Error("internal error - index out of range: " + index); 480 } 481 482 /** 483 * Return a vector containing the references of the Feature objects within 484 * the given range of indices. 485 * @param start_index The index of the first feature to return. 486 * @param end_index The index of the last feature to return. 487 **/ getFeaturesInIndexRange(final int start_index, final int end_index)488 public FeatureVector getFeaturesInIndexRange(final int start_index, 489 final int end_index) 490 { 491 final FeatureVector return_vector = new FeatureVector(); 492 493 for(int i = start_index; i <= end_index; ++i) 494 return_vector.add(featureAt(i)); 495 496 return return_vector; 497 } 498 499 /** 500 * Return a vector containing the references of the Feature objects within 501 * the given range for all the active entries in the SimpleEntryGroup. 502 * @param range Return features that overlap this range - ie the start of 503 * the feature is less than or equal to the end of the range and the end 504 * of the feature is greater than or equal to the start of the range. 505 * @return The non-source key features of this feature table the are within 506 * the given range. The returned object is a copy - changes will not 507 * effect the FeatureTable object itself. 508 **/ getFeaturesInRange(Range range)509 public FeatureVector getFeaturesInRange(Range range) 510 throws OutOfRangeException 511 { 512 final FeatureVector return_vector = new FeatureVector(); 513 final int my_size = size(); 514 515 for(int i = 0; i < my_size; ++i) 516 { 517 final Entry this_entry = elementAt(i); 518 519 if(isActive(this_entry)) 520 { 521 final FeatureVector visible_entry_features = 522 elementAt(i).getFeaturesInRange(range); 523 524 final int visible_entry_features_size = visible_entry_features.size(); 525 526 for(int feature_index = 0; feature_index < visible_entry_features_size; 527 ++feature_index) 528 { 529 final Feature this_feature = 530 visible_entry_features.elementAt(feature_index); 531 return_vector.add(this_feature); 532 } 533 } 534 } 535 536 return return_vector; 537 } 538 539 /** 540 * Return a vector containing the references of the Feature objects from 541 * all the active entries in the SimpleEntryGroup. 542 * @return The non-source key features in active entries of this 543 * SimpleEntryGroup. The returned object is a copy - changes will not 544 * effect the SimpleEntryGroup object itself. 545 **/ getAllFeatures()546 public FeatureVector getAllFeatures() 547 { 548 final FeatureVector return_vector = new FeatureVector(); 549 final int my_size = size(); 550 551 for(int i = 0; i < my_size; ++i) 552 { 553 final Entry this_entry = elementAt(i); 554 555 if(isActive(this_entry)) 556 { 557 final FeatureVector entry_features = elementAt(i).getAllFeatures(); 558 final int entry_features_size = entry_features.size(); 559 560 for(int feature_index = 0; feature_index < entry_features_size; 561 ++feature_index) 562 { 563 final Feature this_feature = 564 entry_features.elementAt(feature_index); 565 return_vector.add(this_feature); 566 } 567 } 568 } 569 570 return return_vector; 571 } 572 573 /** 574 * Return a count of the number of Feature objects from all the active 575 * entries in the SimpleEntryGroup. 576 * @return A count of the non-source key features in active entries of this 577 * SimpleEntryGroup. 578 **/ getAllFeaturesCount()579 public int getAllFeaturesCount() 580 { 581 int return_count = 0; 582 final int my_size = size(); 583 584 for(int i = 0; i < my_size; ++i) 585 { 586 final Entry this_entry = elementAt(i); 587 588 if(isActive(this_entry)) 589 return_count += this_entry.getFeatureCount(); 590 } 591 592 return return_count; 593 } 594 595 /** 596 * Add an Entry to this object and then emit the appropriate EntryChange 597 * events. 598 **/ addElement(Entry entry)599 public void addElement(Entry entry) 600 { 601 super.addElement(entry); 602 603 // set the default Entry to whichever Entry gets added first 604 if(default_entry == null) 605 default_entry = entry; 606 607 active_entries.add(entry); 608 609 // now inform the listeners that an addition has occured 610 final EntryGroupChangeEvent event = 611 new EntryGroupChangeEvent(this, entry, 612 EntryGroupChangeEvent.ENTRY_ADDED); 613 614 fireEvent(entry_group_listener_list, event); 615 616 // make the new entry the default entry if and only if there was no entry 617 // previously or there was only one entry previously and it contained 618 // sequence but no features 619 if(size() == 1) 620 setDefaultEntry(entry); 621 else 622 { 623 if(size() == 2) 624 { 625 final Entry first_entry = elementAt(0); 626 627 if(first_entry.getFeatureCount() == 0) 628 { 629 final Bases first_entry_bases = first_entry.getBases(); 630 631 if(first_entry_bases != null && 632 first_entry_bases.getLength() > 0) 633 setDefaultEntry(entry); 634 } 635 } 636 } 637 638 entry.addEntryChangeListener(this); 639 entry.addFeatureChangeListener(this); 640 } 641 642 /** 643 * A convenience method that does the same as addElement(Entry). 644 **/ add(final Entry entry)645 public void add(final Entry entry) 646 { 647 if(entry.getEMBLEntry() instanceof IndexedGFFDocumentEntry) 648 ((IndexedGFFDocumentEntry)entry.getEMBLEntry()).setEntryGroup(this); 649 else if(entry.getEMBLEntry() instanceof GFFDocumentEntry) 650 { 651 ((GFFDocumentEntry)entry.getEMBLEntry()).adjustCoordinates( getSequenceEntry() ); 652 if(!Options.isBlackBeltMode() && size() > 1 && 653 entry.getEMBLEntry().getSequence() != null ) 654 { 655 new MessageDialog (null, "Warning", 656 "Overlaying a GFF with a sequence onto an entry with a sequence.", 657 false); 658 } 659 } 660 661 addElement(entry); 662 } 663 664 /** 665 * Remove an Entry from this object and then emit the appropriate 666 * EntryGroupChange events. The first entry in the group can only be 667 * removed if it is the only Entry because the first Entry contains the 668 * sequence. 669 * @return true if the removal succeeded, false if it fails(which can if 670 * the given Entry isn't in this SimpleEntryGroup or if the user tries to 671 * remove the first Entry). 672 **/ removeElement(final Entry entry)673 public boolean removeElement(final Entry entry) 674 { 675 // this call will sort out the default entry 676 setIsActive(indexOf(entry), false); 677 678 entry.dispose(); 679 680 final boolean remove_return = super.removeElement(entry); 681 682 entry.removeEntryChangeListener(this); 683 entry.removeFeatureChangeListener(this); 684 685 active_entries.removeElement(entry); 686 687 // now inform the listeners that a deletion has occured 688 final EntryGroupChangeEvent event = 689 new EntryGroupChangeEvent(this, entry, 690 EntryGroupChangeEvent.ENTRY_DELETED); 691 692 fireEvent(entry_group_listener_list, event); 693 694 return remove_return; 695 } 696 697 /** 698 * A convenience method that does the same as removeElement(Entry). 699 **/ remove(final Entry entry)700 public boolean remove(final Entry entry) 701 { 702 return removeElement(entry); 703 } 704 705 /** 706 * Create a new(blank) Feature in the default Entry of this 707 * SimpleEntryGroup. See getDefaultEntry() and Entry.createFeature(). 708 * @return The new Feature. 709 **/ createFeature()710 public Feature createFeature() throws ReadOnlyException 711 { 712 final Feature new_feature = getDefaultEntry().createFeature(); 713 return new_feature; 714 } 715 716 /** 717 * Create a new(empty) Entry in this SimpleEntryGroup. See 718 * Entry.newEntry(). 719 * @return The reference of the new Entry. 720 **/ createEntry()721 public Entry createEntry() 722 { 723 Entry new_entry = null; 724 Entry default_entry = getDefaultEntry(); 725 if(default_entry != null && 726 default_entry.getEMBLEntry() != null && 727 default_entry.getEMBLEntry() instanceof DatabaseDocumentEntry) 728 { 729 DatabaseDocument doc = 730 (DatabaseDocument)((DocumentEntry)default_entry.getEMBLEntry()).getDocument(); 731 DatabaseDocument new_doc = doc.createDatabaseDocument(); 732 733 try 734 { 735 DatabaseDocumentEntry new_doc_entry = 736 new DatabaseDocumentEntry(); 737 new_doc_entry.setDocument(new_doc); 738 new_entry = new Entry(getBases(), new_doc_entry); 739 } 740 catch(Exception e) 741 { 742 e.printStackTrace(); 743 } 744 745 } 746 else 747 new_entry = Entry.newEntry(getBases()); 748 749 add(new_entry); 750 return new_entry; 751 } 752 753 /** 754 * Create a new(empty) Entry in this SimpleEntryGroup. See 755 * Entry.newEntry(). 756 * @param name The(file) name of the new Entry. 757 * @return The reference of the new Entry. 758 **/ createEntry(final String name)759 public Entry createEntry(final String name) 760 { 761 final Entry new_entry = createEntry(); 762 new_entry.setName(name); 763 return new_entry; 764 } 765 766 /** 767 * Returns an enumeration of the Feature objects in this 768 * SimpleEntryGroup. The returned FeatureEnumeration object will generate 769 * all features in this object in turn. The first item generated is the 770 * item at index 0, then the item at index 1, and so on. 771 **/ features()772 public FeatureEnumeration features() 773 { 774 return new FeatureEnumerator(); 775 } 776 777 /** 778 * An Enumeration of Feature objects. 779 **/ 780 public class FeatureEnumerator implements FeatureEnumeration 781 { 782 783 /** 784 * The EntryVector object that we are enumerating. Set to null when there 785 * are no more Feature objects. 786 **/ 787 private EntryVector active_entries; 788 789 /** 790 * The index of the Entry that we will get the next Feature from. 791 **/ 792 private int entry_index = -1; 793 794 /** Enumeration for the current entry */ 795 private FeatureEnumeration feature_enumerator; 796 797 /** 798 * Create a new FeatureEnumeration that will enumerate the enclosing 799 * SimpleEntryGroup object. The SimpleEntryGroup object must not be 800 * changed while the enumeration is active. 801 **/ FeatureEnumerator()802 public FeatureEnumerator() 803 { 804 active_entries = getActiveEntries(); 805 806 entry_index = 0; 807 808 if(active_entries.size() > 0) 809 feature_enumerator = 810 active_entries.elementAt(entry_index).features(); 811 else 812 feature_enumerator = null; 813 } 814 815 /** 816 * See the FeatureEnumeration interface for details. 817 **/ hasMoreFeatures()818 public boolean hasMoreFeatures() 819 { 820 if(feature_enumerator == null) 821 return false; 822 823 if(feature_enumerator.hasMoreFeatures()) 824 return true; 825 826 ++entry_index; 827 828 if(entry_index == active_entries.size()) 829 return false; 830 else 831 { 832 feature_enumerator = 833 active_entries.elementAt(entry_index).features(); 834 return hasMoreFeatures(); 835 } 836 } 837 838 /** 839 * See the FeatureEnumeration interface for details. 840 **/ nextFeature()841 public Feature nextFeature() 842 throws NoSuchElementException 843 { 844 if(feature_enumerator == null) 845 throw new NoSuchElementException(); 846 847 return feature_enumerator.nextFeature(); 848 } 849 850 } 851 852 /** 853 * Implementation of the FeatureChangeListener interface. We listen for 854 * changes in every feature of every entry in this group. 855 **/ featureChanged(FeatureChangeEvent event)856 public void featureChanged(FeatureChangeEvent event) 857 { 858 // pass the action straight through 859 fireEvent(feature_listener_list, event); 860 } 861 862 /** 863 * Implementation of the EntryChangeListener interface. We listen for 864 * changes from every entry in this group and pass the events though to all 865 * the object listening for EntryChangeEvents for the event from this 866 * SimpleEntryGroup. 867 **/ entryChanged(EntryChangeEvent event)868 public void entryChanged(EntryChangeEvent event) 869 { 870 // pass the action straight through 871 fireEvent(entry_listener_list, event); 872 } 873 874 /** 875 * Send an event to those object listening for it. 876 * @param listeners A Vector of the objects that the event should be sent 877 * to. 878 * @param event The event to send 879 **/ fireEvent(Vector listeners, ChangeEvent event)880 private void fireEvent(Vector listeners, ChangeEvent event) 881 { 882 final Vector targets; 883 // copied from a book - synchronising the whole method might cause a 884 // deadlock 885 synchronized(this) 886 { 887 targets = (Vector)listeners.clone(); 888 } 889 890 //boolean seen_chado_manager = false; 891 final int targets_size = targets.size(); 892 for(int i = 0; i < targets_size; ++i) 893 { 894 ChangeListener target =(ChangeListener) targets.elementAt(i); 895 896 if(event instanceof EntryGroupChangeEvent) 897 { 898 final EntryGroupChangeListener entry_group_change_listener = 899 (EntryGroupChangeListener) target; 900 final EntryGroupChangeEvent group_change_event = 901 (EntryGroupChangeEvent) event; 902 entry_group_change_listener.entryGroupChanged(group_change_event); 903 } 904 else 905 { 906 if(event instanceof EntryChangeEvent) 907 { 908 final EntryChangeListener entry_change_listener = 909 (EntryChangeListener) target; 910 911 // if(entry_change_listener instanceof ChadoTransactionManager) 912 // { 913 // just call this listener once 914 // if(!seen_chado_manager) 915 // { 916 // entry_change_listener.entryChanged((EntryChangeEvent) event); 917 // seen_chado_manager = true; 918 // } 919 // } 920 // else 921 entry_change_listener.entryChanged((EntryChangeEvent) event); 922 } 923 else 924 { 925 final FeatureChangeListener feature_change_listener = 926 (FeatureChangeListener) target; 927 feature_change_listener.featureChanged((FeatureChangeEvent) event); 928 } 929 930 } 931 } 932 } 933 934 /** 935 * Adds the specified event listener to receive entry group change events 936 * from this object. 937 * @param l the event change listener. 938 **/ addEntryGroupChangeListener(EntryGroupChangeListener l)939 public void addEntryGroupChangeListener(EntryGroupChangeListener l) 940 { 941 entry_group_listener_list.addElement(l); 942 } 943 944 /** 945 * Removes the specified event listener so that it no longer receives 946 * entry group change events from this object. 947 * @param l the event change listener. 948 **/ removeEntryGroupChangeListener(EntryGroupChangeListener l)949 public void removeEntryGroupChangeListener(EntryGroupChangeListener l) 950 { 951 entry_group_listener_list.removeElement(l); 952 } 953 954 /** 955 * Adds the specified event listener to receive entry change events from 956 * this object. 957 * @param l the event change listener. 958 **/ addEntryChangeListener(EntryChangeListener l)959 public void addEntryChangeListener(EntryChangeListener l) 960 { 961 entry_listener_list.addElement(l); 962 } 963 964 /** 965 * Removes the specified event listener so that it no longer receives 966 * entry change events from this object. 967 * @param l the event change listener. 968 **/ removeEntryChangeListener(EntryChangeListener l)969 public void removeEntryChangeListener(EntryChangeListener l) 970 { 971 entry_listener_list.removeElement(l); 972 } 973 974 /** 975 * Adds the specified event listener to receive feature change events from 976 * this object. 977 * @param l the event change listener. 978 **/ addFeatureChangeListener(FeatureChangeListener l)979 public void addFeatureChangeListener(FeatureChangeListener l) 980 { 981 feature_listener_list.addElement(l); 982 } 983 984 /** 985 * Removes the specified event listener so that it no longer receives 986 * feature change events from this object. 987 * @param l the event change listener. 988 **/ removeFeatureChangeListener(FeatureChangeListener l)989 public void removeFeatureChangeListener(FeatureChangeListener l) 990 { 991 feature_listener_list.removeElement(l); 992 } 993 994 /** 995 * Return the reference of an EntryVector containing the active entries of 996 * this EntryGroup. 997 **/ getActiveEntries()998 public EntryVector getActiveEntries() 999 { 1000 return(EntryVector) active_entries.clone(); 1001 } 1002 1003 /** 1004 * This method translates the start and end of every each Range in every 1005 * Location into another coordinate system. The Ranges will be truncated 1006 * if necessary. 1007 * @param constraint This contains the start and end base of the new 1008 * coordinate system. The position given by constraint.getStart() will 1009 * be at postion/base 1 in the new coordinate system. 1010 * @return a copy of the EntryGroup which has been translated into the new 1011 * coordinate system. 1012 **/ truncate(final Range constraint)1013 public EntryGroup truncate(final Range constraint) 1014 { 1015 final Bases new_bases = getBases().truncate(constraint); 1016 1017 final EntryGroup new_entry_group = new SimpleEntryGroup(new_bases); 1018 final int my_size = size(); 1019 1020 for(int i = 0; i < my_size; ++i) 1021 { 1022 final Entry this_entry = elementAt(i); 1023 final Entry new_entry = this_entry.truncate(new_bases, constraint); 1024 new_entry_group.add(new_entry); 1025 } 1026 1027 if(size() > 0) 1028 { 1029 final StreamSequence sequence = 1030 (StreamSequence) new_bases.getSequence(); 1031 final SimpleDocumentEntry document_entry = 1032 (SimpleDocumentEntry) new_entry_group.elementAt(0).getEMBLEntry(); 1033 document_entry.setSequence(sequence); 1034 } 1035 1036 return new_entry_group; 1037 } 1038 1039 /** 1040 * Return the ActionController for this EntryGroup(for undo). 1041 **/ getActionController()1042 public ActionController getActionController() 1043 { 1044 return action_controller; 1045 } 1046 1047 } 1048