This is an automated email from the ASF dual-hosted git repository. rec pushed a commit to branch bugfix/346-Helper-annotation-created-by-subiterator-may-remain-in-CAS in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git
commit 6347db2f3f457a8d0cab82f11ba2c97036b42a2f Author: Richard Eckart de Castilho <[email protected]> AuthorDate: Mon Sep 18 11:27:05 2023 +0200 Issue #346: Helper annotation created by subiterator may remain in CAS - Use internal helper method from SelectFSs_impl to produce temporary annotation - Clean up test class --- .../org/apache/uima/cas/impl/SelectFSs_impl.java | 5 + .../java/org/apache/uima/cas/impl/Subiterator.java | 4 +- .../org/apache/uima/cas/impl/SubiteratorTest.java | 119 ++++++++++++++++++ .../org/apache/uima/cas/test/SubiteratorTest.java | 135 --------------------- 4 files changed, 126 insertions(+), 137 deletions(-) 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 77ff9bb07..d059c60cc 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.impl.Subiterator.BoundsUse; 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 @@ public class SelectFSs_impl<T extends FeatureStructure> implements SelectFSs<T> // } 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 63c55673f..613b30c86 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 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T> 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 @@ public class Subiterator<T extends AnnotationFS> implements LowLevelIterator<T> */ 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 000000000..7f3cd95ab --- /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 dbdefcafb..000000000 --- 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); - } -}
