shashank created CAMEL-26437:
--------------------------------
Summary: camel-util - FilterIterator.remove after a failed next
removes an element that next never returned
Key: CAMEL-26437
URL: https://issues.apache.org/jira/browse/CAMEL-26437
Project: Camel
Issue Type: Bug
Components: camel-core
Reporter: shashank
Assignee: shashank
Follow-up of CAMEL-26290 (apache/camel#28643), which made
{{FilterIterator.remove()}} throw {{IllegalStateException}} after a
{{hasNext()}} look-ahead, because the look-ahead moves the wrapped iterator
past the element {{next()}} returned.
{{next()}} looks ahead in the same way when it has no element ready:
{{checkNext()}} calls {{it.next()}} on the wrapped iterator until the filter
accepts an element. If that does not end with an accepted element, {{next()}}
fails, but the permission to remove from the previous successful {{next()}} is
kept, while the wrapped iterator is now on another element. A {{remove()}} then
removes that element, which {{next()}} never returned:
{code:java}
List<Integer> list = new ArrayList<>(List.of(1, 2));
FilterIterator<Integer> it = new FilterIterator<>(list.iterator(), n -> n == 1);
it.next(); // 1
it.next(); // looks at 2, rejects it, throws NoSuchElementException
it.remove(); // removes 2; list is now [1]
{code}
The same happens when the filter throws while it looks at the next element, and
when the elements left are null (they are skipped). The javadoc of {{remove()}}
says it removes the last element returned by {{next()}}.
Found by an OpenJML/JML verification pilot of the {{FilterIterator}} contract
(the removal-safety condition failed on the original source and holds with the
change below), then reproduced with plain JUnit tests.
*Proposal*
* In {{checkNext()}}, set {{canRemove = false}} right before each
{{it.next()}}: once the wrapped iterator moves, the element returned last can
no longer be removed. A successful {{next()}} sets it again. A {{next()}} that
fails at the end of the wrapped iterator, without moving it, keeps the element
returned before removable (as a list iterator does).
* Tests: {{NoSuchElementException}} after a rejected tail, a throwing filter, a
skipped null tail, and controls (end of the wrapped iterator, {{hasNext()}}
before the failed {{next()}}).
* Fold one paragraph into the existing 4.24 upgrade guide entry of CAMEL-26290.
_Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)