Author: scolebourne Date: Sat Jul 16 10:08:16 2005 New Revision: 219343 URL: http://svn.apache.org/viewcvs?rev=219343&view=rev Log: TreeList/CursorableLinkedList/NodeCachingLinkedList/AbstractLinkedList - Fix iterator remove not working properly when called after previous
bug 35258 Modified: jakarta/commons/proper/collections/trunk/RELEASE-NOTES.html jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/AbstractLinkedList.java jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/CursorableLinkedList.java jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/TreeList.java jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/AbstractTestList.java jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/TestCursorableLinkedList.java Modified: jakarta/commons/proper/collections/trunk/RELEASE-NOTES.html URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/collections/trunk/RELEASE-NOTES.html?rev=219343&r1=219342&r2=219343&view=diff ============================================================================== --- jakarta/commons/proper/collections/trunk/RELEASE-NOTES.html (original) +++ jakarta/commons/proper/collections/trunk/RELEASE-NOTES.html Sat Jul 16 10:08:16 2005 @@ -75,6 +75,7 @@ <li>FastArrayList - Fix iterators and views to work better in multithreaded environments</li> <li>FastArrayList - Fix iterator remove where ConcurrentModificationException not as expected [34690]</li> <li>CursorableLinkedList (list subpackage) - Fix iterator remove/set not throwing IllegalStateException after next-previous-removeByIndex [35766]</li> +<li>TreeList/CursorableLinkedList/NodeCachingLinkedList/AbstractLinkedList - Fix iterator remove not working properly when called after previous [35258]</li> <li>SetUniqueList.set(int,Object) - Destroyed set status in certain circumstances [33294]</li> <li>AbstractLinkedMap.init() - Now calls createEntry() to create the map entry object [33706]</li> <li>AbstractHashedMap deserialization - Fix to prevent doubling of internal data array [34265]</li> Modified: jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/AbstractLinkedList.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/AbstractLinkedList.java?rev=219343&r1=219342&r2=219343&view=diff ============================================================================== --- jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/AbstractLinkedList.java (original) +++ jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/AbstractLinkedList.java Sat Jul 16 10:08:16 2005 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2004 The Apache Software Foundation + * Copyright 2001-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -819,9 +819,16 @@ public void remove() { checkModCount(); - parent.removeNode(getLastNodeReturned()); + if (current == next) { + // remove() following previous() + next = next.next; + parent.removeNode(getLastNodeReturned()); + } else { + // remove() following next() + parent.removeNode(getLastNodeReturned()); + nextIndex--; + } current = null; - nextIndex--; expectedModCount++; } @@ -885,13 +892,13 @@ */ protected static class LinkedSubList extends AbstractList { /** The main list */ - private AbstractLinkedList parent; + AbstractLinkedList parent; /** Offset from the main list */ - private int offset; + int offset; /** Sublist size */ - private int size; + int size; /** Sublist modCount */ - private int expectedModCount; + int expectedModCount; protected LinkedSubList(AbstractLinkedList parent, int fromIndex, int toIndex) { if (fromIndex < 0) { Modified: jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/CursorableLinkedList.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/CursorableLinkedList.java?rev=219343&r1=219342&r2=219343&view=diff ============================================================================== --- jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/CursorableLinkedList.java (original) +++ jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/CursorableLinkedList.java Sat Jul 16 10:08:16 2005 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2004 The Apache Software Foundation + * Copyright 2002-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ * methods provides access to a <code>Cursor</code> instance which extends * <code>ListIterator</code>. The cursor allows changes to the list concurrent * with changes to the iterator. Note that the [EMAIL PROTECTED] #iterator()} method and - * sublists do <b>not</b> provide this cursor behaviour. + * sublists do <b>not</b> provide this cursor behaviour. * <p> * The <code>Cursor</code> class is provided partly for backwards compatibility * and partly because it allows the cursor to be directly closed. Closing the @@ -376,6 +376,19 @@ //----------------------------------------------------------------------- /** + * Creates a list iterator for the sublist. + * + * @param subList the sublist to get an iterator for + * @param fromIndex the index to start from, relative to the sublist + */ + protected ListIterator createSubListListIterator(LinkedSubList subList, int fromIndex) { + SubCursor cursor = new SubCursor(subList, fromIndex); + registerCursor(cursor); + return cursor; + } + + //----------------------------------------------------------------------- + /** * An extended <code>ListIterator</code> that allows concurrent changes to * the underlying list. */ @@ -384,6 +397,8 @@ boolean valid = true; /** Is the next index valid */ boolean nextIndexValid = true; + /** Flag to indicate if the current element was removed by another object. */ + boolean currentRemovedByAnother = false; /** * Constructs a new cursor. @@ -394,7 +409,33 @@ super(parent, index); valid = true; } - + + /** + * Removes the item last returned by this iterator. + * <p> + * There may have been subsequent alterations to the list + * since you obtained this item, however you can still remove it. + * You can even remove it if the item is no longer in the main list. + * However, you can't call this method on the same iterator more + * than once without calling next() or previous(). + * + * @throws IllegalStateException if there is no item to remove + */ + public void remove() { + // overridden, as the nodeRemoved() method updates the iterator + // state in the parent.removeNode() call below + if (current == null && currentRemovedByAnother) { + // quietly ignore, as the last returned node was removed + // by the list or some other iterator + // by ignoring it, we keep this iterator independent from + // other changes as much as possible + } else { + checkModCount(); + parent.removeNode(getLastNodeReturned()); + } + currentRemovedByAnother = false; + } + /** * Adds an object to the list. * The object added here will be the new 'previous' in the iterator. @@ -402,10 +443,17 @@ * @param obj the object to add */ public void add(Object obj) { + // overridden, as the nodeInserted() method updates the iterator state super.add(obj); - // add on iterator does not return the added element + // matches the (next.previous == node) clause in nodeInserted() + // thus next gets changed - reset it again here next = next.next; } + + // set is not overridden, as it works ok + // note that we want it to throw an exception if the element being + // set has been removed from the real list (compare this with the + // remove method where we silently ignore this case) /** * Gets the index of the next element to be returned. @@ -449,17 +497,21 @@ // state where next() followed by previous() next = node.next; current = null; + currentRemovedByAnother = true; } else if (node == next) { // state where next() not followed by previous() // and we are matching next node next = node.next; + currentRemovedByAnother = false; } else if (node == current) { // state where next() not followed by previous() // and we are matching current (last returned) node current = null; + currentRemovedByAnother = true; nextIndex--; } else { nextIndexValid = false; + currentRemovedByAnother = false; } } @@ -502,4 +554,49 @@ } } } + + //----------------------------------------------------------------------- + /** + * A cursor for the sublist based on LinkedSubListIterator. + */ + protected static class SubCursor extends Cursor { + + /** The parent list */ + protected final LinkedSubList sub; + + /** + * Constructs a new cursor. + * + * @param index the index to start from + */ + protected SubCursor(LinkedSubList sub, int index) { + super((CursorableLinkedList) sub.parent, index + sub.offset); + this.sub = sub; + } + + public boolean hasNext() { + return (nextIndex() < sub.size); + } + + public boolean hasPrevious() { + return (previousIndex() >= 0); + } + + public int nextIndex() { + return (super.nextIndex() - sub.offset); + } + + public void add(Object obj) { + super.add(obj); + sub.expectedModCount = parent.modCount; + sub.size++; + } + + public void remove() { + super.remove(); + sub.expectedModCount = parent.modCount; + sub.size--; + } + } + } Modified: jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/TreeList.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/TreeList.java?rev=219343&r1=219342&r2=219343&view=diff ============================================================================== --- jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/TreeList.java (original) +++ jakarta/commons/proper/collections/trunk/src/java/org/apache/commons/collections/list/TreeList.java Sat Jul 16 10:08:16 2005 @@ -749,23 +749,20 @@ /** The parent list */ protected final TreeList parent; /** - * The node that will be returned by [EMAIL PROTECTED] #next()}. If this is equal - * to [EMAIL PROTECTED] AbstractLinkedList#header} then there are no more values to return. + * Cache of the next node that will be returned by [EMAIL PROTECTED] #next()}. */ protected AVLNode next; /** - * The index of [EMAIL PROTECTED] #next}. + * The index of the next node to be returned. */ protected int nextIndex; /** - * The last node that was returned by [EMAIL PROTECTED] #next()} or [EMAIL PROTECTED] - * #previous()}. Set to <code>null</code> if [EMAIL PROTECTED] #next()} or [EMAIL PROTECTED] - * #previous()} haven't been called, or if the node has been removed - * with [EMAIL PROTECTED] #remove()} or a new node added with [EMAIL PROTECTED] #add(Object)}. + * Cache of the last node that was returned by [EMAIL PROTECTED] #next()} + * or [EMAIL PROTECTED] #previous()}. */ protected AVLNode current; /** - * The index of [EMAIL PROTECTED] #current}. + * The index of the last node that was returned. */ protected int currentIndex; /** @@ -788,6 +785,7 @@ this.expectedModCount = parent.modCount; this.next = (parent.root == null ? null : parent.root.get(fromIndex)); this.nextIndex = fromIndex; + this.currentIndex = -1; } /** @@ -852,13 +850,20 @@ public void remove() { checkModCount(); - if (current == null) { + if (currentIndex == -1) { throw new IllegalStateException(); } - parent.remove(currentIndex); + if (nextIndex == currentIndex) { + // remove() following previous() + next = next.next(); + parent.remove(currentIndex); + } else { + // remove() following next() + parent.remove(currentIndex); + nextIndex--; + } current = null; currentIndex = -1; - nextIndex--; expectedModCount++; } Modified: jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/AbstractTestList.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/AbstractTestList.java?rev=219343&r1=219342&r2=219343&view=diff ============================================================================== --- jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/AbstractTestList.java (original) +++ jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/AbstractTestList.java Sat Jul 16 10:08:16 2005 @@ -1,5 +1,5 @@ /* - * Copyright 2001-2004 The Apache Software Foundation + * Copyright 2001-2005 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -796,10 +796,38 @@ } } + //----------------------------------------------------------------------- + /** + * Tests remove on list iterator is correct. + */ + public void testListListIteratorPreviousRemoveNext() { + if (isRemoveSupported() == false) return; + resetFull(); + ListIterator it = getList().listIterator(); + Object zero = it.next(); + Object one = it.next(); + Object two = it.next(); + Object two2 = it.previous(); + Object one2 = it.previous(); + assertSame(one, one2); + assertSame(two, two2); + assertSame(zero, getList().get(0)); + assertSame(one, getList().get(1)); + assertSame(two, getList().get(2)); + + it.remove(); // removed element at index 1 (one) + assertSame(zero, getList().get(0)); + assertSame(two, getList().get(1)); + Object two3 = it.next(); // do next after remove + assertSame(two, two3); + assertEquals(collection.size() > 2, it.hasNext()); + assertEquals(true, it.hasPrevious()); + } + /** * Tests remove on list iterator is correct. */ - public void testListListIteratorPreviousRemove() { + public void testListListIteratorPreviousRemovePrevious() { if (isRemoveSupported() == false) return; resetFull(); ListIterator it = getList().listIterator(); @@ -813,11 +841,64 @@ assertSame(zero, getList().get(0)); assertSame(one, getList().get(1)); assertSame(two, getList().get(2)); - it.remove(); + + it.remove(); // removed element at index 1 (one) assertSame(zero, getList().get(0)); assertSame(two, getList().get(1)); + Object zero3 = it.previous(); // do previous after remove + assertSame(zero, zero3); + assertEquals(false, it.hasPrevious()); + assertEquals(collection.size() > 2, it.hasNext()); } + /** + * Tests remove on list iterator is correct. + */ + public void testListListIteratorNextRemoveNext() { + if (isRemoveSupported() == false) return; + resetFull(); + ListIterator it = getList().listIterator(); + Object zero = it.next(); + Object one = it.next(); + Object two = it.next(); + assertSame(zero, getList().get(0)); + assertSame(one, getList().get(1)); + assertSame(two, getList().get(2)); + Object three = getList().get(3); + + it.remove(); // removed element at index 2 (two) + assertSame(zero, getList().get(0)); + assertSame(one, getList().get(1)); + Object three2 = it.next(); // do next after remove + assertSame(three, three2); + assertEquals(collection.size() > 3, it.hasNext()); + assertEquals(true, it.hasPrevious()); + } + + /** + * Tests remove on list iterator is correct. + */ + public void testListListIteratorNextRemovePrevious() { + if (isRemoveSupported() == false) return; + resetFull(); + ListIterator it = getList().listIterator(); + Object zero = it.next(); + Object one = it.next(); + Object two = it.next(); + assertSame(zero, getList().get(0)); + assertSame(one, getList().get(1)); + assertSame(two, getList().get(2)); + + it.remove(); // removed element at index 2 (two) + assertSame(zero, getList().get(0)); + assertSame(one, getList().get(1)); + Object one2 = it.previous(); // do previous after remove + assertSame(one, one2); + assertEquals(true, it.hasNext()); + assertEquals(true, it.hasPrevious()); + } + + //----------------------------------------------------------------------- /** * Traverses to the end of the given iterator. * Modified: jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/TestCursorableLinkedList.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/TestCursorableLinkedList.java?rev=219343&r1=219342&r2=219343&view=diff ============================================================================== --- jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/TestCursorableLinkedList.java (original) +++ jakarta/commons/proper/collections/trunk/src/test/org/apache/commons/collections/list/TestCursorableLinkedList.java Sat Jul 16 10:08:16 2005 @@ -460,8 +460,17 @@ assertEquals(true, c1.nextIndexValid); assertEquals(1, c1.nextIndex); + assertEquals(true, c1.currentRemovedByAnother); assertEquals(null, c1.current); assertEquals("C", c1.next.value); + + assertEquals("[A, C]", list.toString()); + c1.remove(); // works ok + assertEquals("[A, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} } public void testInternalState_CursorNextRemoveIndex1ByList() { @@ -476,8 +485,17 @@ assertEquals(true, c1.nextIndexValid); assertEquals(1, c1.nextIndex); + assertEquals(false, c1.currentRemovedByAnother); assertEquals("A", c1.current.value); assertEquals("C", c1.next.value); + + assertEquals("[A, C]", list.toString()); + c1.remove(); // works ok + assertEquals("[C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} } public void testInternalState_CursorNextNextRemoveIndex1ByList() { @@ -493,8 +511,17 @@ assertEquals(true, c1.nextIndexValid); assertEquals(1, c1.nextIndex); + assertEquals(true, c1.currentRemovedByAnother); assertEquals(null, c1.current); assertEquals("C", c1.next.value); + + assertEquals("[A, C]", list.toString()); + c1.remove(); // works ok + assertEquals("[A, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} } public void testInternalState_CursorNextNextNextRemoveIndex1ByList() { @@ -511,8 +538,267 @@ assertEquals("B", list.remove(1)); assertEquals(false, c1.nextIndexValid); + assertEquals(false, c1.currentRemovedByAnother); assertEquals("C", c1.current.value); assertEquals("D", c1.next.value); + + assertEquals("[A, C, D]", list.toString()); + c1.remove(); // works ok + assertEquals("[A, D]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} + } + + //----------------------------------------------------------------------- + public void testInternalState_CursorNextNextPreviousRemoveByIterator() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + assertEquals("B", c1.next()); + assertEquals("B", c1.previous()); + + c1.remove(); + + assertEquals(true, c1.nextIndexValid); + assertEquals(1, c1.nextIndex); + assertEquals(false, c1.currentRemovedByAnother); + assertEquals(null, c1.current); + assertEquals("C", c1.next.value); + + assertEquals("[A, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} + } + + public void testInternalState_CursorNextNextRemoveByIterator() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + assertEquals("B", c1.next()); + + c1.remove(); + + assertEquals(true, c1.nextIndexValid); + assertEquals(1, c1.nextIndex); + assertEquals(false, c1.currentRemovedByAnother); + assertEquals(null, c1.current); + assertEquals("C", c1.next.value); + + assertEquals("[A, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} + } + + //----------------------------------------------------------------------- + public void testInternalState_CursorNextNextPreviousAddIndex1ByList() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + assertEquals("B", c1.next()); + assertEquals("B", c1.previous()); + + list.add(1, "Z"); + + assertEquals(true, c1.nextIndexValid); + assertEquals(1, c1.nextIndex); + assertEquals("B", c1.current.value); + assertEquals("Z", c1.next.value); + + assertEquals("[A, Z, B, C]", list.toString()); + c1.remove(); // works ok + assertEquals("[A, Z, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} + } + + public void testInternalState_CursorNextAddIndex1ByList() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + + list.add(1, "Z"); + + assertEquals(true, c1.nextIndexValid); + assertEquals(1, c1.nextIndex); + assertEquals("A", c1.current.value); + assertEquals("Z", c1.next.value); + + assertEquals("[A, Z, B, C]", list.toString()); + c1.remove(); // works ok + assertEquals("[Z, B, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} + } + + public void testInternalState_CursorNextNextAddIndex1ByList() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + assertEquals("B", c1.next()); + + list.add(1, "Z"); + + assertEquals(false, c1.nextIndexValid); + assertEquals("B", c1.current.value); + assertEquals("C", c1.next.value); + + assertEquals("[A, Z, B, C]", list.toString()); + c1.remove(); // works ok + assertEquals("[A, Z, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} + } + + //----------------------------------------------------------------------- + public void testInternalState_CursorNextNextPreviousAddByIterator() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + assertEquals("B", c1.next()); + assertEquals("B", c1.previous()); + + c1.add("Z"); + + assertEquals(true, c1.nextIndexValid); + assertEquals(2, c1.nextIndex); + assertEquals(null, c1.current); + assertEquals("B", c1.next.value); + + assertEquals("[A, Z, B, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} + } + + public void testInternalState_CursorNextNextAddByIterator() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + assertEquals("B", c1.next()); + + c1.add("Z"); + + assertEquals(true, c1.nextIndexValid); + assertEquals(3, c1.nextIndex); + assertEquals(false, c1.currentRemovedByAnother); + assertEquals(null, c1.current); + assertEquals("C", c1.next.value); + + assertEquals("[A, B, Z, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} + } + + //----------------------------------------------------------------------- + public void testInternalState_CursorNextNextRemoveByListSetByIterator() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + assertEquals("B", c1.next()); + + list.remove(1); + + assertEquals(true, c1.nextIndexValid); + assertEquals(1, c1.nextIndex); + assertEquals(null, c1.current); + assertEquals("C", c1.next.value); + assertEquals("[A, C]", list.toString()); + + try { + c1.set("Z"); + fail(); + } catch (IllegalStateException ex) {} + } + + //----------------------------------------------------------------------- + public void testInternalState_CursorNextNextPreviousSetByIterator() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + assertEquals("B", c1.next()); + assertEquals("B", c1.previous()); + + c1.set("Z"); + + assertEquals(true, c1.nextIndexValid); + assertEquals(1, c1.nextIndex); + assertEquals("Z", c1.current.value); + assertEquals("Z", c1.next.value); + + assertEquals("[A, Z, C]", list.toString()); + c1.remove(); // works ok + assertEquals("[A, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} + } + + public void testInternalState_CursorNextNextSetByIterator() { + list.add("A"); + list.add("B"); + list.add("C"); + + CursorableLinkedList.Cursor c1 = list.cursor(); + assertEquals("A", c1.next()); + assertEquals("B", c1.next()); + + c1.set("Z"); + + assertEquals(true, c1.nextIndexValid); + assertEquals(2, c1.nextIndex); + assertEquals("Z", c1.current.value); + assertEquals("C", c1.next.value); + + assertEquals("[A, Z, C]", list.toString()); + c1.remove(); // works ok + assertEquals("[A, C]", list.toString()); + try { + c1.remove(); + fail(); + } catch (IllegalStateException ex) {} } //----------------------------------------------------------------------- --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]