Merge branch 'main' into merge/update-main-to-3.4.x
diff --git a/uimaj-core/src/main/java/org/apache/uima/cas/impl/SelectFSs_impl.java b/uimaj-core/src/main/java/org/apache/uima/cas/impl/SelectFSs_impl.java
index 77ff9bb..d059c60 100644
--- a/uimaj-core/src/main/java/org/apache/uima/cas/impl/SelectFSs_impl.java
+++ b/uimaj-core/src/main/java/org/apache/uima/cas/impl/SelectFSs_impl.java
@@ -56,6 +56,7 @@
 import org.apache.uima.cas.text.AnnotationFS;
 import org.apache.uima.cas.text.AnnotationIndex;
 import org.apache.uima.cas.text.AnnotationPredicates;
+import org.apache.uima.jcas.JCas;
 import org.apache.uima.jcas.cas.EmptyFSList;
 import org.apache.uima.jcas.cas.FSArray;
 import org.apache.uima.jcas.cas.FSList;
@@ -1018,6 +1019,10 @@
   // }
 
   private Annotation makePosAnnot(int begin, int end) {
+    return makePosAnnot(jcas, begin, end);
+  }
+
+  static Annotation makePosAnnot(JCas jcas, int begin, int end) {
     if (end < begin) {
       throw new IllegalArgumentException("End value must be >= Begin value");
     }
diff --git a/uimaj-core/src/main/java/org/apache/uima/cas/impl/Subiterator.java b/uimaj-core/src/main/java/org/apache/uima/cas/impl/Subiterator.java
index 63c5567..613b30c 100644
--- a/uimaj-core/src/main/java/org/apache/uima/cas/impl/Subiterator.java
+++ b/uimaj-core/src/main/java/org/apache/uima/cas/impl/Subiterator.java
@@ -365,7 +365,7 @@
       if (begin < 0) {
         begin = 0;
       }
-      coveringStartPos = new Annotation(jcas, begin, Integer.MAX_VALUE);
+      coveringStartPos = SelectFSs_impl.makePosAnnot(jcas, begin, Integer.MAX_VALUE);
     } else {
       coveringStartPos = null;
     }
@@ -759,7 +759,7 @@
    */
   private void moveToJustPastBoundsAndBackup(int begin, int end,
           Predicate<Annotation> continue_going_backwards) {
-    it.moveToNoReinit(new Annotation(jcas, begin, end));
+    it.moveToNoReinit(SelectFSs_impl.makePosAnnot(jcas, begin, end));
 
     if (!it.isValid()) {
       it.moveToLastNoReinit();
diff --git a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SubiteratorTest.java b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SubiteratorTest.java
new file mode 100644
index 0000000..7f3cd95
--- /dev/null
+++ b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SubiteratorTest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.uima.cas.impl;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.contentOf;
+
+import java.io.File;
+
+import org.apache.uima.UIMAFramework;
+import org.apache.uima.analysis_engine.AnalysisEngine;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.cas.FSIterator;
+import org.apache.uima.cas.test.Sentence;
+import org.apache.uima.cas.test.Token;
+import org.apache.uima.jcas.JCas;
+import org.apache.uima.resource.ResourceSpecifier;
+import org.apache.uima.test.junit_extension.JUnitExtension;
+import org.apache.uima.util.XMLInputSource;
+import org.apache.uima.util.XMLParser;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+//@formatter:off
+/**
+ * The setup:
+ *   Token (super = Annotation)
+ *   Sentence (super = Annotation)
+ *   
+ *   Annotator:  (in descr) SubIteratorAnnotator
+ */
+//@formatter:on
+public class SubiteratorTest {
+
+  private static AnalysisEngine segmenter = null;
+
+  private JCas jcas;
+
+  @BeforeAll
+  static void setupClass() throws Exception {
+    File descriptorFile = JUnitExtension.getFile("CASTests/desc/TokensAndSentences.xml");
+
+    assertThat(descriptorFile).exists();
+
+    XMLParser parser = UIMAFramework.getXMLParser();
+    ResourceSpecifier spec = (ResourceSpecifier) parser.parse(new XMLInputSource(descriptorFile));
+    segmenter = UIMAFramework.produceAnalysisEngine(spec);
+  }
+
+  @BeforeEach
+  public void setUp() throws Exception {
+    String text = contentOf(getClass().getResource("/CASTests/verjuice.txt"), UTF_8);
+
+    jcas = segmenter.newJCas();
+    jcas.setDocumentText(text);
+
+    segmenter.process(jcas);
+  }
+
+  @Test
+  public void testAnnotator() throws Exception {
+    iterateAndCheck(jcas);
+
+    iterateAndCheck(jcas);
+  }
+
+  @Test
+  public void thatTemporaryAnnotationsAreNotRetained() throws Exception {
+    var casImpl = ((CASImpl) jcas.getCas());
+    try (var ctx = casImpl.ll_forceEnableV2IdRefs(true)) {
+      var fsesBefore = new AllFSs(casImpl).getAllFSsAllViews_sofas_reachable().getAllFSsSorted();
+      var maxId = fsesBefore.stream().mapToInt(fs -> fs._id).max().getAsInt();
+
+      // This select creates a temporary annotation used to constrain the operation
+      jcas.select(Token.class).covering(0, 10).asList();
+
+      var fsesAfter = new AllFSs(casImpl).getAllFSsAllViews_sofas_reachable().getAllFSsSorted();
+      assertThat(fsesAfter) //
+              .extracting(fs -> fs.getType().getName())//
+              .containsOnly(CAS.TYPE_NAME_SOFA, CAS.TYPE_NAME_DOCUMENT_ANNOTATION,
+                      Sentence._TypeName, Token._TypeName);
+
+      // The +1 here accounts for the temporary Annotation that was created.
+      var t = new Token(jcas);
+      assertThat(t._id).isEqualTo(maxId + 2 + 1);
+    }
+  }
+
+  private void iterateAndCheck(JCas aJCas) {
+    var tokenIndex = aJCas.getAnnotationIndex(Token.class);
+    var firstSentence = aJCas.getAnnotationIndex(Sentence.class).iterator().next();
+    var tokenIterator = tokenIndex.subiterator(firstSentence);
+    var firstToken = tokenIndex.iterator().next();
+    tokenIterator.moveTo(firstToken);
+
+    // check unambiguous iterator creation
+    FSIterator<Token> it = tokenIndex.iterator(false);
+    it.moveTo(firstToken);
+  }
+}
diff --git a/uimaj-core/src/test/java/org/apache/uima/cas/test/SubiteratorTest.java b/uimaj-core/src/test/java/org/apache/uima/cas/test/SubiteratorTest.java
deleted file mode 100644
index dbdefca..0000000
--- a/uimaj-core/src/test/java/org/apache/uima/cas/test/SubiteratorTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *   http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.uima.cas.test;
-
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.apache.uima.UIMAFramework;
-import org.apache.uima.analysis_engine.AnalysisEngine;
-import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
-import org.apache.uima.cas.FSIterator;
-import org.apache.uima.cas.text.AnnotationIndex;
-import org.apache.uima.jcas.JCas;
-import org.apache.uima.jcas.tcas.Annotation;
-import org.apache.uima.resource.ResourceInitializationException;
-import org.apache.uima.resource.ResourceSpecifier;
-import org.apache.uima.test.junit_extension.JUnitExtension;
-import org.apache.uima.util.FileUtils;
-import org.apache.uima.util.InvalidXMLException;
-import org.apache.uima.util.XMLInputSource;
-import org.apache.uima.util.XMLParser;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-//@formatter:off
-/**
- * The setup:
- *   Token (super = Annotation)
- *   Sentence (super = Annotation)
- *   
- *   Annotator:  (in descr) SubIteratorAnnotator
- */
-//@formatter:on
-public class SubiteratorTest {
-
-  private AnalysisEngine ae = null;
-
-  @BeforeEach
-  public void setUp() {
-    File descriptorFile = JUnitExtension.getFile("CASTests/desc/TokensAndSentences.xml");
-    assertTrue("Descriptor must exist: " + descriptorFile.getAbsolutePath(),
-            descriptorFile.exists());
-
-    try {
-      XMLParser parser = UIMAFramework.getXMLParser();
-      ResourceSpecifier spec = (ResourceSpecifier) parser.parse(new XMLInputSource(descriptorFile));
-      this.ae = UIMAFramework.produceAnalysisEngine(spec);
-    } catch (IOException e) {
-      e.printStackTrace();
-      assertTrue(false);
-    } catch (InvalidXMLException e) {
-      e.printStackTrace();
-      assertTrue(false);
-    } catch (ResourceInitializationException e) {
-      e.printStackTrace();
-      assertTrue(false);
-    }
-
-  }
-
-  @AfterEach
-  public void tearDown() {
-    if (this.ae != null) {
-      this.ae.destroy();
-      this.ae = null;
-    }
-  }
-
-  @Test
-  public void testAnnotator() {
-    File textFile = JUnitExtension.getFile("CASTests/verjuice.txt");
-    String text = null;
-    try {
-      text = FileUtils.file2String(textFile, "utf-8");
-    } catch (IOException e) {
-      e.printStackTrace();
-      assertTrue(false);
-    }
-    JCas jcas = null;
-    try {
-      jcas = this.ae.newJCas();
-    } catch (ResourceInitializationException e) {
-      e.printStackTrace();
-      assertTrue(false);
-    }
-    jcas.setDocumentText(text);
-    try {
-      this.ae.process(jcas);
-
-      iterateAndcheck(jcas);
-
-      iterateAndcheck(jcas);
-    } catch (AnalysisEngineProcessException e) {
-      e.printStackTrace();
-      assertTrue(false);
-    } catch (ClassCastException e) {
-      // UIMA-464: Subiterator.moveTo() throws ClassCastException.
-      assertTrue(false);
-    }
-  }
-
-  private void iterateAndcheck(JCas jcas) {
-    AnnotationIndex<Token> tokenIndex = jcas.getAnnotationIndex(Token.class);
-    Annotation sentence = jcas.getAnnotationIndex(Sentence.class).iterator().next();
-    FSIterator<Token> tokenIterator = tokenIndex.subiterator(sentence);
-    Annotation token = tokenIndex.iterator().next();
-    // debug token.toString();
-    tokenIterator.moveTo(token); // throws ClassCastException
-
-    // check unambiguous iterator creation
-
-    FSIterator<Token> it = tokenIndex.iterator(false);
-    it.moveTo(token);
-  }
-}