Merge pull request #236 from apache/bugfix/235-Misleading-error-message-when-JCas-type-is-not-registered

Issue #235: Misleading error message when JCas type is not registered
diff --git a/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes6.java b/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes6.java
index 6d0cb9f..e6bcec2 100644
--- a/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes6.java
+++ b/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes6.java
@@ -3044,7 +3044,7 @@
     if (!isWrite) {
       // always have form 6 do just reachables, to mimic what v2 did
       AllFSs allFSs;
-      try (AutoCloseableNoException a = LowLevelCAS.ll_defaultV2IdRefs(false)) {
+      try (AutoCloseableNoException a = cas1.ll_enableV2IdRefs(false)) {
         allFSs = new AllFSs(cas1, mark, isTypeMapping ? fs -> isTypeInTgt(fs) : null,
                 isTypeMapping ? typeMapper : null).getAllFSsAllViews_sofas_reachable();
         // AllFSs internally already causes _save_to_cas_data() to be called, so we have to add all
diff --git a/uimaj-core/src/main/java/org/apache/uima/cas/impl/CASImpl.java b/uimaj-core/src/main/java/org/apache/uima/cas/impl/CASImpl.java
index b8c6236..e3f57f5 100644
--- a/uimaj-core/src/main/java/org/apache/uima/cas/impl/CASImpl.java
+++ b/uimaj-core/src/main/java/org/apache/uima/cas/impl/CASImpl.java
@@ -5240,8 +5240,10 @@
     List<TOP> all = new AllFSs(this, mark, includeFilter, typeMapper)
             .getAllFSsAllViews_sofas_reachable().getAllFSsSorted();
     List<TOP> filtered = filterAboveMark(all, mark);
-    for (TOP fs : filtered) {
-      action_filtered.accept(fs);
+    if (action_filtered != null) {
+      for (TOP fs : filtered) {
+        action_filtered.accept(fs);
+      }
     }
     return all;
   }
@@ -6169,6 +6171,13 @@
     return r;
   }
 
+  AutoCloseableNoException ll_forceEnableV2IdRefs(boolean enable) {
+    final boolean restoreState = svd.isId2Fs;
+    AutoCloseableNoException r = () -> svd.isId2Fs = restoreState;
+    svd.isId2Fs = enable;
+    return r;
+  }
+
   // int allocIntData(int sz) {
   //
   // if (sz > INT_DATA_FOR_ALLOC_SIZE / 4) {
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 0322c2d..c0d6aba 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
@@ -63,6 +63,7 @@
 import org.apache.uima.jcas.cas.TOP;
 import org.apache.uima.jcas.impl.JCasImpl;
 import org.apache.uima.jcas.tcas.Annotation;
+import org.apache.uima.util.AutoCloseableNoException;
 
 // @formatter:off
 /**
@@ -1020,7 +1021,9 @@
     if (end < begin) {
       throw new IllegalArgumentException("End value must be >= Begin value");
     }
-    return new Annotation(jcas, begin, end);
+    try (AutoCloseableNoException c = ((CASImpl) jcas.getCas()).ll_forceEnableV2IdRefs(false)) {
+      return new Annotation(jcas, begin, end);
+    }
   }
 
 //@formatter:off
diff --git a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsNoJCasTest.java b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsNoJCasTest.java
index 79bc7d8..cac79b5 100644
--- a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsNoJCasTest.java
+++ b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SelectFsNoJCasTest.java
@@ -19,7 +19,7 @@
 
 package org.apache.uima.cas.impl;
 
-import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
 
 import java.io.File;
 
@@ -30,9 +30,11 @@
 import org.apache.uima.resource.metadata.TypeSystemDescription;
 import org.apache.uima.resource.metadata.impl.TypePriorities_impl;
 import org.apache.uima.test.junit_extension.JUnitExtension;
+import org.apache.uima.util.AutoCloseableNoException;
 import org.apache.uima.util.CasCreationUtils;
 import org.apache.uima.util.XMLInputSource;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 // tests without initializing JCas
@@ -53,16 +55,33 @@
             null);
   }
 
+  @BeforeEach
+  public void setup() {
+    cas.reset();
+  }
+
   @Test
   public void testOpsNeedingAnnotation() {
     Type type = cas.getTypeSystem().getType("x.y.z.SentenceNoJCas");
     FeatureStructure s = cas.createAnnotation(type, 0, 4);
     cas.indexRepository.addFS(s);
 
-    boolean b = cas.<Annotation> select(type).covering(1, 2).map(f -> f.getBegin()).findFirst()
+    assertThat(cas.<Annotation> select(type).covering(1, 2).map(f -> f.getBegin()).findFirst()) //
             .isPresent();
-
-    assertTrue(b);
   }
 
+  @Test
+  public void thatHelperAnnotationsDoNotRemainInCas() {
+    try (AutoCloseableNoException a = cas.ll_enableV2IdRefs(true)) {
+      cas.setDocumentText("text");
+      assertThat(cas.walkReachablePlusFSsSorted(null, null, null, null))
+              .containsExactly(cas.getSofa(), cas.getDocumentAnnotation());
+
+      cas.select(Annotation.class).at(0, 1);
+
+      assertThat(cas.walkReachablePlusFSsSorted(null, null, null, null))
+              .as("The helper annotation created by select must not be discovarably")
+              .containsExactly(cas.getSofa(), cas.getDocumentAnnotation());
+    }
+  }
 }
diff --git a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SerDesForm6Test.java b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SerDesForm6Test.java
index 65f8344..5b963b6 100644
--- a/uimaj-core/src/test/java/org/apache/uima/cas/impl/SerDesForm6Test.java
+++ b/uimaj-core/src/test/java/org/apache/uima/cas/impl/SerDesForm6Test.java
@@ -66,7 +66,6 @@
 import org.apache.uima.jcas.cas.TOP;
 import org.apache.uima.resource.ResourceInitializationException;
 import org.apache.uima.resource.metadata.TypeSystemDescription;
-import org.apache.uima.util.AutoCloseableNoException;
 import org.apache.uima.util.CasCreationUtils;
 import org.apache.uima.util.impl.SerializationMeasures;
 import org.junit.jupiter.api.AfterEach;
@@ -420,15 +419,6 @@
     }
   }
 
-  @Test
-  public void testAllKindsV2() {
-    try (AutoCloseableNoException a = LowLevelCAS.ll_defaultV2IdRefs();
-            AutoCloseableNoException b = casSrc.ll_enableV2IdRefs()) { // because casSrc set in
-                                                                       // setup
-      testAllKinds();
-    }
-  }
-
   // Test chains going through filtered type
   // Repeat below with OneType, and TwoTypes with filtered slot == fsRef
 
diff --git a/uimaj-core/src/test/java/org/apache/uima/util/CasIOUtilsTest.java b/uimaj-core/src/test/java/org/apache/uima/util/CasIOUtilsTest.java
index 345ebf0..d386b1c 100644
--- a/uimaj-core/src/test/java/org/apache/uima/util/CasIOUtilsTest.java
+++ b/uimaj-core/src/test/java/org/apache/uima/util/CasIOUtilsTest.java
@@ -19,6 +19,7 @@
 package org.apache.uima.util;
 
 import static java.util.Arrays.asList;
+import static org.apache.uima.cas.SerialFormat.COMPRESSED_FILTERED_TSI;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.io.ByteArrayInputStream;
@@ -32,14 +33,18 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.uima.UIMAFramework;
 import org.apache.uima.cas.CAS;
 import org.apache.uima.cas.CASRuntimeException;
 import org.apache.uima.cas.FeatureStructure;
 import org.apache.uima.cas.SerialFormat;
+import org.apache.uima.cas.impl.CASImpl;
 import org.apache.uima.jcas.cas.TOP;
+import org.apache.uima.jcas.tcas.Annotation;
 import org.apache.uima.resource.metadata.FsIndexDescription;
 import org.apache.uima.resource.metadata.TypeDescription;
 import org.apache.uima.resource.metadata.TypeSystemDescription;
@@ -337,6 +342,41 @@
             .extracting(fs -> fs.getType().getName()).containsExactly(customDocAnnoTypeName);
   }
 
+  @Test
+  public void thatBinaryForm6DoesOnlyIncludeReachableFSes() throws Exception {
+    CASImpl cas = (CASImpl) CasCreationUtils.createCas();
+    byte[] buf;
+    try (AutoCloseableNoException a = cas.ll_enableV2IdRefs(true)) {
+      Annotation ann = cas.createAnnotation(cas.getAnnotationType(), 0, 1);
+      ann.addToIndexes();
+      ann.removeFromIndexes();
+
+      Set<FeatureStructure> allFSes = new LinkedHashSet<>();
+      cas.walkReachablePlusFSsSorted(allFSes::add, null, null, null);
+
+      assertThat(allFSes) //
+              .as("The annotation that was added and then removed before serialization should be found") //
+              .containsExactly(cas.getSofa(), ann);
+
+      ByteArrayOutputStream bos = new ByteArrayOutputStream();
+      CasIOUtils.save(cas, bos, COMPRESSED_FILTERED_TSI);
+      buf = bos.toByteArray();
+    }
+
+    cas.reset();
+
+    try (AutoCloseableNoException a = cas.ll_enableV2IdRefs(true)) {
+      CasIOUtils.load(new ByteArrayInputStream(buf), cas);
+
+      Set<FeatureStructure> allFSes = new LinkedHashSet<>();
+      cas.walkReachablePlusFSsSorted(allFSes::add, null, null, null);
+
+      assertThat(allFSes) //
+              .as("The annotation that was added and then removed before serialization should not be found") //
+              .containsExactly(cas.getSofa());
+    }
+  }
+
   @AfterEach
   public void tearDown() throws Exception {
     cas.release();