You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by sc...@apache.org on 2014/12/12 06:14:05 UTC

svn commit: r1644829 - in /uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima: cas/ cas/impl/ jcas/ jcas/impl/

Author: schor
Date: Fri Dec 12 05:14:04 2014
New Revision: 1644829

URL: http://svn.apache.org/r1644829
Log:
[UIMA-4146][UIMA-4135][UIMA-4126] support snapshot iterators, support for modifying indexed FSs, fix delta cas deserialization to not corrupt indices

Added:
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSsTobeAddedback.java
Modified:
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/CAS.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/AnnotationIndexImpl.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes4.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes6.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/CASImpl.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSIndexRepositoryImpl.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasDeserializer.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasSerializer.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiSerializationSharedData.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/JCas.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/CAS.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/CAS.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/CAS.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/CAS.java Fri Dec 12 05:14:04 2014
@@ -1048,5 +1048,29 @@ public interface CAS extends AbstractCas
    * @return a marker object.
    */
   Marker createMarker();  
+  /**
+   * Call this method to set up a region, 
+   * ended by a {@link FSsTobeAddedback.close()} call on the returned object,
+   * You can use this or the {@link #protectIndices(Runnable)} method to protected
+   * the indices.
+   * <p>
+   * This approach allows arbitrary code between  the protectIndices and the associated close method.
+   * <p>
+   * The close method is best done in a finally block, or using the try-with-resources statement in 
+   * Java 8.
+   * 
+   * @return an object used to record things that need adding back
+   */
+  AutoCloseable protectIndices();
+  
+  /**
+   * Runs the code in the runnable inside a protection block, where any modifications to features
+   * done while in this block will be done in a way to protect any indices which otherwise 
+   * might become corrupted by the update action; the protection is achieved by temporarily
+   * removing the FS (if it is in the indices), before the update happens.
+   * At the end of the block, affected indices have any removed-under-the-covers FSs added back.
+   * @param runnable code to execute while protecting the indices. 
+   */
+  void protectIndices(Runnable runnable);
 
 }

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/AnnotationIndexImpl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/AnnotationIndexImpl.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/AnnotationIndexImpl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/AnnotationIndexImpl.java Fri Dec 12 05:14:04 2014
@@ -171,4 +171,9 @@ public class AnnotationIndexImpl<T exten
     }
   }
 
+  @Override
+  public FSIndex withSnapshotIterators() {
+    return new AnnotationIndexImpl(index.withSnapshotIterators());
+  }
+
 }

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes4.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes4.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes4.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes4.java Fri Dec 12 05:14:04 2014
@@ -76,6 +76,7 @@ import org.apache.uima.cas.AbstractCas;
 import org.apache.uima.cas.CASRuntimeException;
 import org.apache.uima.cas.Feature;
 import org.apache.uima.cas.Marker;
+import org.apache.uima.cas.impl.FSsTobeAddedback.FSsTobeAddedbackSingle;
 import org.apache.uima.internal.util.IntVector;
 import org.apache.uima.jcas.JCas;
 import org.apache.uima.util.impl.DataIO;
@@ -2023,21 +2024,22 @@ public class BinaryCasSerDes4 {
      * Modified heap values need fsStartIndexes conversion
      ******************************************************************************/
 
-    class ReadModifiedFSs {
+    private class ReadModifiedFSs {
       
       // previous value - for things diff encoded
-      int vPrevModInt = 0;
-      int vPrevModHeapRef = 0;
-      short vPrevModShort = 0;
-      long vPrevModLong = 0;
-      int iHeap;
-      TypeInfo typeInfo;
- 
-
+      private int vPrevModInt = 0;
+      private int vPrevModHeapRef = 0;
+      private short vPrevModShort = 0;
+      private long vPrevModLong = 0;
+      private int iHeap;
+      private TypeInfo typeInfo;
+      
+      // next for managing index removes / readds
+      private boolean wasRemoved;
+      private FSsTobeAddedbackSingle addbackSingle;
+      private int[] featCodes;
 
       private void readModifiedFSs() throws IOException {
-        final List<FSIndexRepositoryImpl> toBeAddedRepos = new ArrayList<FSIndexRepositoryImpl>();
-
         final int modFSsLength = readVnumber(control_dis);
         iPrevHeap = 0;
                  
@@ -2057,10 +2059,15 @@ public class BinaryCasSerDes4 {
             readModifiedAuxHeap(numberOfModsInThisFs);
           } else {
             // https://issues.apache.org/jira/browse/UIMA-4100
-            toBeAddedRepos.clear();
-            cas.removeFromCorruptableIndexAnyView(iHeap, toBeAddedRepos);
-            readModifiedMainHeap(numberOfModsInThisFs);
-            cas.addBackRemovedFsToAppropViews(iHeap,  toBeAddedRepos);
+            // see if any of the mods are keys
+            featCodes = cas.getTypeSystemImpl().ll_getAppropriateFeatures(tCode);
+//            cas.removeFromCorruptableIndexAnyView(iHeap, indexToDos);
+            try {
+              wasRemoved = false;
+              readModifiedMainHeap(numberOfModsInThisFs);
+            } finally {
+              cas.addbackSingle(iHeap);
+            }
           }
         }
       }
@@ -2099,6 +2106,12 @@ public class BinaryCasSerDes4 {
       
       private void readModifiedMainHeap(int numberOfMods) throws IOException {
         int iPrevOffsetInFs = 0;
+        
+        wasRemoved = false;  // set to true when removed from index to stop further testing
+        addbackSingle = cas.getAddbackSingle();
+        addbackSingle.clear();
+        
+
         for (int i = 0; i < numberOfMods; i++) {
           final int offsetInFs = readVnumber(fsIndexes_dis) + iPrevOffsetInFs;
           iPrevOffsetInFs = offsetInFs;
@@ -2118,6 +2131,7 @@ public class BinaryCasSerDes4 {
               final int v = readDiff(int_dis, vPrevModInt);
               vPrevModInt = v;
               heap[iHeap + offsetInFs] = v;
+              maybeRemove(offsetInFs);
             }
             break;
           case Slot_Short: {
@@ -2139,15 +2153,24 @@ public class BinaryCasSerDes4 {
             break;
           case Slot_Float:
             heap[iHeap + offsetInFs] = readFloat();
+            maybeRemove(offsetInFs);
             break;
           case Slot_StrRef:
             heap[iHeap + offsetInFs] = readString();
+            maybeRemove(offsetInFs);
             break;
          default:
             throw new RuntimeException();
           }
         }   
       }
+      
+      private void maybeRemove(int srcOffsetInFs) {
+        if (!typeInfo.isHeapStoredArray && !wasRemoved) {
+          wasRemoved |= cas.removeFromCorruptableIndexAnyView(iHeap, addbackSingle, featCodes[srcOffsetInFs - 1]);
+        }
+      }    
+
     }
   }
 

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes6.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes6.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes6.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/BinaryCasSerDes6.java Fri Dec 12 05:14:04 2014
@@ -70,6 +70,7 @@ import java.util.zip.InflaterInputStream
 
 import org.apache.uima.cas.AbstractCas;
 import org.apache.uima.cas.CASRuntimeException;
+import org.apache.uima.cas.impl.FSsTobeAddedback.FSsTobeAddedbackSingle;
 import org.apache.uima.cas.impl.SlotKinds.SlotKind;
 import org.apache.uima.cas.impl.TypeSystemImpl.TypeInfo;
 import org.apache.uima.internal.util.IntVector;
@@ -2244,16 +2245,21 @@ public class BinaryCasSerDes6 {
    * Modified heap values need fsStartIndexes conversion
    ******************************************************************************/
 
-  class ReadModifiedFSs {
+  private class ReadModifiedFSs {
     
     // previous value - for things diff encoded
-    int vPrevModInt = 0;
-    int prevModHeapRefTgtSeq = 0;
-    short vPrevModShort = 0;
-    long vPrevModLong = 0;
-    int iHeap;
-    TypeInfo typeInfo;
-    int[] tgtF2srcF;
+    private int vPrevModInt = 0;
+    private int prevModHeapRefTgtSeq = 0;
+    private short vPrevModShort = 0;
+    private long vPrevModLong = 0;
+    private int iHeap;
+    private TypeInfo typeInfo;
+    private int[] tgtF2srcF;
+    
+    // next for managing index removes / readds
+    private boolean wasRemoved;
+    private FSsTobeAddedbackSingle addbackSingle;
+    private int[] featCodes;
     
     // for handling aux heaps with type mapping which may skip some things in the target
     //   An amount that needs to be added to the offset from target to account for
@@ -2269,7 +2275,6 @@ public class BinaryCasSerDes6 {
 //      int[] srcNextSkippedIndex;
 
     private void readModifiedFSs() throws IOException {
-      final List<FSIndexRepositoryImpl> toBeAddedRepos = new ArrayList<FSIndexRepositoryImpl>();
       final int modFSsLength = readVnumber(control_dis);
       int prevSeq = 0;
       
@@ -2319,10 +2324,14 @@ public class BinaryCasSerDes6 {
            readModifiedAuxHeap(numberOfModsInThisFs);
         } else {
           // https://issues.apache.org/jira/browse/UIMA-4100
-          toBeAddedRepos.clear();
-          cas.removeFromCorruptableIndexAnyView(iHeap, toBeAddedRepos);
-          readModifiedMainHeap(numberOfModsInThisFs);
-          cas.addBackRemovedFsToAppropViews(iHeap,  toBeAddedRepos);       
+          featCodes = cas.getTypeSystemImpl().ll_getAppropriateFeatures(tCode);
+//          cas.removeFromCorruptableIndexAnyView(iHeap, indexToDos);
+          try {
+            wasRemoved = false;
+            readModifiedMainHeap(numberOfModsInThisFs);
+          } finally {
+            cas.addbackSingle(iHeap);
+          }   
         }
       }
     }
@@ -2369,6 +2378,11 @@ public class BinaryCasSerDes6 {
     private void readModifiedMainHeap(int numberOfMods) throws IOException {
       
       int iPrevTgtOffsetInFs = 0;
+      wasRemoved = false;  // set to true when removed from index to stop further testing
+      addbackSingle = cas.getAddbackSingle();
+      addbackSingle.clear();
+
+      
       for (int i = 0; i < numberOfMods; i++) {
         final int tgtOffsetInFs = readVnumber(fsIndexes_dis) + iPrevTgtOffsetInFs;
         iPrevTgtOffsetInFs = tgtOffsetInFs;
@@ -2393,6 +2407,7 @@ public class BinaryCasSerDes6 {
             final int v = readDiff(int_dis, vPrevModInt);
             vPrevModInt = v;
             heap[iHeap + srcOffsetInFs] = v;
+            maybeRemove(srcOffsetInFs);
           }
           break;
         case Slot_Short: {
@@ -2413,21 +2428,31 @@ public class BinaryCasSerDes6 {
           }
           break;
         case Slot_Byte: case Slot_Boolean:
-          heap[iHeap + tgtOffsetInFs] = byte_dis.readByte();
+          heap[iHeap + srcOffsetInFs] = byte_dis.readByte();
           break;
         case Slot_Float:
-          heap[iHeap + tgtOffsetInFs] = readFloat();
+          heap[iHeap + srcOffsetInFs] = readFloat();
+          maybeRemove(srcOffsetInFs);
           break;
         case Slot_StrRef:
-          heap[iHeap + tgtOffsetInFs] = readString(true);
+          heap[iHeap + srcOffsetInFs] = readString(true);
+          maybeRemove(srcOffsetInFs);
           break;
        default:
           throw new RuntimeException();
         }
       }   
     }
+    
+    private void maybeRemove(int srcOffsetInFs) {
+      if (!typeInfo.isHeapStoredArray && !wasRemoved) {
+        wasRemoved |= cas.removeFromCorruptableIndexAnyView(iHeap, addbackSingle, featCodes[srcOffsetInFs - 1]);
+      }
+    }    
+    
   }
   
+  
 
   /* *******************************************************************
    * methods common to serialization / deserialization etc.

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/CASImpl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/CASImpl.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/CASImpl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/CASImpl.java Fri Dec 12 05:14:04 2014
@@ -23,6 +23,8 @@ import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.ByteBuffer;
@@ -41,6 +43,7 @@ import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.uima.UIMAFramework;
 import org.apache.uima.cas.AbstractCas_ImplBase;
 import org.apache.uima.cas.ArrayFS;
 import org.apache.uima.cas.BooleanArrayFS;
@@ -77,7 +80,7 @@ import org.apache.uima.cas.admin.CASMgr;
 import org.apache.uima.cas.admin.FSIndexComparator;
 import org.apache.uima.cas.admin.FSIndexRepositoryMgr;
 import org.apache.uima.cas.admin.TypeSystemMgr;
-import org.apache.uima.cas.impl.FSIndexRepositoryImpl.IndexRepoTodos;
+import org.apache.uima.cas.impl.FSsTobeAddedback.FSsTobeAddedbackSingle;
 import org.apache.uima.cas.text.AnnotationFS;
 import org.apache.uima.cas.text.AnnotationIndex;
 import org.apache.uima.cas.text.Language;
@@ -85,7 +88,9 @@ import org.apache.uima.internal.util.Int
 import org.apache.uima.internal.util.PositiveIntSet_impl;
 import org.apache.uima.jcas.JCas;
 import org.apache.uima.jcas.impl.JCasImpl;
+import org.apache.uima.jcas.tcas.Annotation;
 import org.apache.uima.resource.ResourceInitializationException;
+import org.apache.uima.util.Level;
 
 /**
  * Implements the CAS interfaces. This class must be public because we need to
@@ -125,10 +130,25 @@ public class CASImpl extends AbstractCas
    *   <li>The following are the same:  -Duima.check_invalid_fs_updates    and -Duima.check_invalid_fs_updates=true</li>
    * </ul> 
    */
-  public static final String CHK_FS_UPDATES_CORRUPTS = "uima.check_fs_update_corrupts_index";
+  public static final String REPORT_FS_UPDATES_CORRUPTS = "uima.report_fs_update_corrupts_index";
 
-  public static final boolean IS_CHK_FS_UPDATE_CORRUPTS_INDEX =  !System.getProperty(CHK_FS_UPDATES_CORRUPTS, "false").equals("false");
+  public static final boolean IS_REPORT_FS_UPDATE_CORRUPTS_INDEX =  !System.getProperty(REPORT_FS_UPDATES_CORRUPTS, "false").equalsIgnoreCase("false");
 
+  /**
+   * Set this JVM property to false for high performance, (no checking);
+   * insure you don't have the report flag (above) turned on - otherwise it will force this to "true".
+   */
+  public static final String PROTECT_INDICES = "uima.protect_indices_from_key_udpates";
+  
+  /**
+   * the protect indices flag is on by default, but may be turned of via setting the property to false.
+   * 
+   * This is overridden (the flag is turned back on) if a report is requested (by above flag).
+   */
+  public static final boolean IS_PROTECT_INDICES = 
+      System.getProperty(PROTECT_INDICES, "true").equalsIgnoreCase("true") ||
+      IS_REPORT_FS_UPDATE_CORRUPTS_INDEX;
+  
   // The offset for the array length cell. An array consists of length+2
   // number
   // of cells, where the first cell contains the type, the second one the
@@ -155,7 +175,7 @@ public class CASImpl extends AbstractCas
 
   private static enum ModifiedHeap { FSHEAP, BYTEHEAP, SHORTHEAP, LONGHEAP };
   // Static classes representing shared instance data
-  // - shared data is computed once
+  // - shared data is computed once for all views
 
   // fields shared among all CASes belong to views of a common base CAS
   private static class SharedViewData {
@@ -259,11 +279,23 @@ public class CASImpl extends AbstractCas
    
     private int cache_not_in_index = 0; // a one item cache of a FS not in the index
     
-    private final PositiveIntSet_impl featureCodesInIndexKeys = 
-       IS_CHK_FS_UPDATE_CORRUPTS_INDEX ? 
-          new PositiveIntSet_impl() : null; 
-                  
-          
+    private final PositiveIntSet_impl featureCodesInIndexKeys = new PositiveIntSet_impl(); 
+    
+    /**
+     * This stack corresponds to nested protectIndices contexts. Normally should be very shallow.
+     */
+    private final List<FSsTobeAddedback> fssTobeAddedback = new ArrayList<FSsTobeAddedback>();
+    
+    /**
+     * This version is for single fs use, by binary deserializers and by automatic mode
+     * Only one user at a time is allowed.
+     */
+    private final FSsTobeAddedbackSingle fsTobeAddedbackSingle = (FSsTobeAddedbackSingle) FSsTobeAddedback.createSingle();
+    /**
+     * Set to true while this is in use.
+     */
+    private boolean fsTobeAddedbackSingleInUse = false;
+    
     private SharedViewData(boolean useFSCache, Heap heap) {
       this.useFSCache = useFSCache;
       this.heap = heap;
@@ -277,6 +309,23 @@ public class CASImpl extends AbstractCas
   // package protected to let other things share this info
   final SharedViewData svd; // shared view data
   
+  void addbackSingle(int fsAddr) {
+    svd.fsTobeAddedbackSingle.addback(fsAddr);
+    svd.fsTobeAddedbackSingleInUse = false;
+  }
+  
+  void resetAddbackSingleInUse() {
+    svd.fsTobeAddedbackSingleInUse = false;
+  }
+  
+  FSsTobeAddedbackSingle getAddbackSingle() {
+    if (svd.fsTobeAddedbackSingleInUse) {
+      throw new RuntimeException(); // internal error
+    }
+    svd.fsTobeAddedbackSingleInUse = true;
+    return svd.fsTobeAddedbackSingle;
+  }
+  
   void featureCodesInIndexKeysAdd(int featCode) {
     svd.featureCodesInIndexKeys.add(featCode);
   }
@@ -528,7 +577,7 @@ public class CASImpl extends AbstractCas
       throw e;
     }
     if (isAnnot) {
-      getLowLevelCAS().ll_setIntValue(addr, ts.annotSofaFeatCode, this.getSofaRef());
+      this.setFeatureValueNotJournaled(addr,  ts.annotSofaFeatCode, this.getSofaRef());
     }
     final FeatureStructure newFS = ll_getFSForRef(addr);
     return newFS;
@@ -725,7 +774,8 @@ public class CASImpl extends AbstractCas
 
   // For internal use only
   public void setSofaFeat(int addr, int sofa) {
-    setFeatureValue(addr, this.svd.casMetadata.ts.annotSofaFeatCode, sofa);
+    // never an index key
+    setFeatureValueNoIndexCorruptionCheck(addr, this.svd.casMetadata.ts.annotSofaFeatCode, sofa);
   }
 
   // For internal use only
@@ -844,6 +894,7 @@ public class CASImpl extends AbstractCas
     return null;
   }
 
+  @Override
   public<T extends FeatureStructure> FSIterator<T> createFilteredIterator(FSIterator<T> it, FSMatchConstraint cons) {
     return new FilteredIterator<T>(it, cons);
   }
@@ -992,6 +1043,8 @@ public class CASImpl extends AbstractCas
       JCasImpl.clearData(this);
     }
     clearTrackingMarks();
+    this.svd.cache_not_in_index = 0;
+    this.svd.fssTobeAddedback.clear();
   }
 
   /**
@@ -1183,23 +1236,25 @@ public class CASImpl extends AbstractCas
 
     reinitIndexedFSs(fsIndex);
   }
-
+  
   
   /**
    * Binary Deserializaion Support
-   * An instance of this class is made and shared  
+   * An instance of this class is made for every reinit operation 
    *
    */
-  private static class BinDeserSupport {
+  private class BinDeserSupport {
     
     private int fsStartAddr;
     private int fsEndAddr;
     private int[] fssAddrArray;
     private int fssIndex;
     private int lastRemovedFsAddr;
-    private List<FSIndexRepositoryImpl> indexRepos = new ArrayList<FSIndexRepositoryImpl>(4);
+    // feature codes - there are exactly the same number as their are features
+    private int[] featCodes;
+    private FSsTobeAddedback tobeAddedback = FSsTobeAddedback.createSingle();
   }
-  
+    
   /**
    * --------------------------------------------------------------------- see
    * Blob Format in CASSerializer
@@ -1328,17 +1383,23 @@ public class CASImpl extends AbstractCas
         bds.fssAddrArray = fss.toArray();
         
         int fsmodssz = readInt(dis, swap);
-        bds.fsStartAddr = -1;
-        final ArrayList<FSIndexRepositoryImpl> indexRepos = new ArrayList<FSIndexRepositoryImpl>();
+        bds.fsStartAddr = -1;        
         
+        // loop over all heap modifications to existing FSs
         
-        for (int i = 0; i < fsmodssz; i++) {
-          final int heapAddrBeingModified = readInt(dis, swap);
-          maybeAddBackAndRemoveFs(heapAddrBeingModified, bds);       
-          this.getHeap().heap[heapAddrBeingModified] = readInt(dis, swap);
+        // first disable auto addbacks for index corruption - this routine is handling that
+        svd.fsTobeAddedbackSingleInUse = true;  // sorry, a bad hack...
+        try {
+          for (int i = 0; i < fsmodssz; i++) {
+            final int heapAddrBeingModified = readInt(dis, swap);
+            maybeAddBackAndRemoveFs(heapAddrBeingModified, bds);       
+            this.getHeap().heap[heapAddrBeingModified] = readInt(dis, swap);
+          }
+          bds.tobeAddedback.addback(bds.lastRemovedFsAddr);
+          bds.fssAddrArray = null;  // free storage
+        } finally {
+          svd.fsTobeAddedbackSingleInUse = false;
         }
-        addBackRemovedFsToAppropViews(bds.lastRemovedFsAddr, indexRepos);
-        bds.fssAddrArray = null;  // free storage
       }
 
       // indexed FSs
@@ -1465,40 +1526,42 @@ public class CASImpl extends AbstractCas
   /**
    * for Deserialization of Delta, when updating existing FSs,
    * If the heap addr is for the next FS, re-add the previous one to those indices where it was removed,
-   * and then remove the new one (and remember which views to re-add to).
+   * and then maybe remove the new one (and remember which views to re-add to).
    * @param heapAddr
    */
   private void maybeAddBackAndRemoveFs(int heapAddr, final BinDeserSupport bds) {
     if (bds.fsStartAddr == -1) {
       bds.fssIndex = -1;
       bds.lastRemovedFsAddr = -1;
+      bds.tobeAddedback.clear();
     }
     findCorrespondingFs(heapAddr, bds); // sets fsStartAddr, end addr
     if (bds.lastRemovedFsAddr != bds.fsStartAddr) {
-      addBackRemovedFsToAppropViews(bds.lastRemovedFsAddr, bds.indexRepos);
-      removeFromCorruptableIndexAnyView(bds.lastRemovedFsAddr = bds.fsStartAddr, bds.indexRepos);
-    }
-  }
-  
-  void addBackRemovedFsToAppropViews(int fsAddr, List<FSIndexRepositoryImpl> indexRepos) {
-    for (FSIndexRepositoryImpl ir : indexRepos) {
-      ir.addFS(fsAddr);
+      bds.tobeAddedback.addback(bds.lastRemovedFsAddr);
+      if (bds.featCodes.length == 0) {
+        // is array
+        final int typeCode = getTypeCode(bds.fsStartAddr);
+        assert(getTypeSystemImpl().ll_isArrayType(typeCode));
+      } else {
+        int featCode = bds.featCodes[heapAddr - (bds.fsStartAddr + 1)];
+        removeFromCorruptableIndexAnyView(bds.lastRemovedFsAddr = bds.fsStartAddr, bds.tobeAddedback, featCode);
+      }
     }
-    indexRepos.clear();
   }
-  
+    
   private void findCorrespondingFs(int heapAddr, final BinDeserSupport bds) {
     if (bds.fsStartAddr < heapAddr && heapAddr < bds.fsEndAddr) {
       return;
     }
     
     // search forward by 1 before doing binary search
-    bds.fssIndex ++;
+    bds.fssIndex ++;  // incrementing dense index into fssAddrArray for start addrs
     bds.fsStartAddr = bds.fssAddrArray[bds.fssIndex];  // must exist
     if (bds.fssIndex + 1 < bds.fssAddrArray.length) { // handle edge case where prev was at the end
       bds.fsEndAddr = bds.fssAddrArray[bds.fssIndex + 1];  // must exist
       if (bds.fsStartAddr < heapAddr && heapAddr < bds.fsEndAddr) {
-       return;
+        bds.featCodes = getTypeSystemImpl().ll_getAppropriateFeatures(getTypeCode(bds.fsStartAddr));
+        return;
       }
     }
     
@@ -1515,6 +1578,7 @@ public class CASImpl extends AbstractCas
     bds.fssIndex = (-result) - 2;
     bds.fsStartAddr = bds.fssAddrArray[bds.fssIndex];
     bds.fsEndAddr = bds.fssAddrArray[bds.fssIndex + 1];
+    bds.featCodes = getTypeSystemImpl().ll_getAppropriateFeatures(getTypeCode(bds.fsStartAddr));    
     assert(bds.fsStartAddr < heapAddr && heapAddr < bds.fsEndAddr);
   }
   
@@ -2001,6 +2065,7 @@ public class CASImpl extends AbstractCas
     return getHeapValue(fsAddr);
   }
   
+  // not journaled, this is for a new FS only
   void copyFeatures(int trgAddr, int srcAddr) throws CASRuntimeException {
     int typeCode = getHeapValue(trgAddr);
     if (typeCode != getHeapValue(srcAddr)) {
@@ -2025,14 +2090,9 @@ public class CASImpl extends AbstractCas
       // if this is a string, create a new reference in the string
       // reference heap
       // and point to the same string as the string feature in src fs.
-      if (isStringType(rangeType)) {
-        int newRef = this.getStringHeap().cloneStringReference(val);
-        // this.getHeap().heap[trgAddr+1+i] = newRef;
-        this.getHeap().heap[trgAddr + this.svd.casMetadata.featureOffset[featCode]] = newRef;
-      } else { // scalar values copied / other FS
-        this.getHeap().heap[trgAddr + this.svd.casMetadata.featureOffset[featCode]] = getHeapValue(srcAddr
-            + this.svd.casMetadata.featureOffset[featCode]);
-      }
+      setFeatureValueNotJournaled(trgAddr,  featCode, isStringType(rangeType) ? 
+            this.getStringHeap().cloneStringReference(val)  : 
+            getHeapValue(srcAddr + this.svd.casMetadata.featureOffset[featCode]));
     }
   }
 
@@ -2048,6 +2108,9 @@ public class CASImpl extends AbstractCas
   }
 
   /**
+   * This is the common point where all sets of values in the heap come through
+   * It implements the check for invalid feature setting and potentially the addback.
+   * 
    * Set the value of a feature of a FS.
    * 
    * @param addr
@@ -2061,12 +2124,47 @@ public class CASImpl extends AbstractCas
    *                    appropriate for the type at the address.
    */
   public void setFeatureValue(int addr, int feat, int val) {
-    this.getHeap().heap[(addr + this.svd.casMetadata.featureOffset[feat])] = val;
+    checkForInvalidFeatureSetting(addr, feat);
+    setFeatureValueNotJournaled(addr, feat, val);
+    maybeAddback(addr);
     if (this.svd.trackingMark != null) {
     	this.logFSUpdate(addr, addr+this.svd.casMetadata.featureOffset[feat], 
     			ModifiedHeap.FSHEAP, 1);
     }
   }
+  
+  /**
+   * Set the value of a feature of a FS without checking for index corruption
+   * (typically because the feature isn't one that can be used as a key, or
+   * the context is one where the FS is being created, and is guaranteed not to be in any index (yet))
+   * 
+   * @param addr    The address of the FS.
+   * @param feat    The code of the feature.
+   * @param val     The new value for the feature.
+   * @exception ArrayIndexOutOfBoundsException
+   *                    If the feature is not a legal feature, or it is not
+   *                    appropriate for the type at the address.
+   */
+  void setFeatureValueNoIndexCorruptionCheck(int addr, int feat, int val) {
+    setFeatureValueNotJournaled(addr, feat, val);
+    if (this.svd.trackingMark != null) {
+      this.logFSUpdate(addr, addr+this.svd.casMetadata.featureOffset[feat], 
+          ModifiedHeap.FSHEAP, 1);
+    }    
+  }
+  /**
+   * Set the value of a feature in the FS without journaling
+   *   (because it's for a new FS above the mark)
+   * @param addr    The address of the FS.
+   * @param feat    The code of the feature.
+   * @param val     The new value for the feature.
+   * @exception ArrayIndexOutOfBoundsException
+   *                    If the feature is not a legal feature, or it is not
+   *                    appropriate for the type at the address.
+   */
+  void setFeatureValueNotJournaled(int addr, int feat, int val) {
+    this.getHeap().heap[(addr + this.svd.casMetadata.featureOffset[feat])] = val;
+  }
 
   public void setStringValue(int addr, int feat, String s) {
     final int stringCode = ((s == null) ? NULL : this.getStringHeap().addString(s));
@@ -2078,10 +2176,13 @@ public class CASImpl extends AbstractCas
     setFeatureValue(addr, feat, floatCode);
   }
 
-  public void setFloatValue(int addr, float f) {
-    final int floatCode = Float.floatToIntBits(f);
-    this.getHeap().heap[addr] = floatCode;
-  }
+  // doesn't do proper index corruption checking
+  // because don't have FS addr
+  // never called 2014
+//  public void setFloatValue(int addr, float f) {
+//    final int floatCode = Float.floatToIntBits(f);
+//    this.getHeap().heap[addr] = floatCode;
+//  }
 
   public int getFeatureValue(int addr, int feat) {
     return this.getHeap().heap[(addr + this.svd.casMetadata.featureOffset[feat])];
@@ -2101,8 +2202,8 @@ public class CASImpl extends AbstractCas
 
   // byte
   public void setFeatureValue(int addr, int feat, byte v) {
-    Byte bytevalue = Byte.valueOf(v);
-    setFeatureValue(addr, feat, bytevalue.intValue());
+    // keys are not byte
+    setFeatureValueNoIndexCorruptionCheck(addr, feat, (int) v);
   }
 
   public byte getByteValue(int addr, int feat) {
@@ -2111,11 +2212,7 @@ public class CASImpl extends AbstractCas
 
   // boolean
   public void setFeatureValue(int addr, int feat, boolean v) {
-    if (v) {
-      setFeatureValue(addr, feat, CASImpl.TRUE);
-    } else {
-      setFeatureValue(addr, feat, CASImpl.FALSE);
-    }
+    setFeatureValueNoIndexCorruptionCheck(addr, feat, v ? CASImpl.TRUE : CASImpl.FALSE);
   }
 
   public boolean getBooleanValue(int addr, int feat) {
@@ -2124,7 +2221,8 @@ public class CASImpl extends AbstractCas
 
   // short
   public void setFeatureValue(int addr, int feat, short s) {
-    setFeatureValue(addr, feat, (int) s);
+    // shorts are not keys
+    setFeatureValueNoIndexCorruptionCheck(addr, feat, (int) s);
   }
 
   public short getShortValue(int addr, int feat) {
@@ -2140,6 +2238,14 @@ public class CASImpl extends AbstractCas
     return this.ll_getLongValue(addr, feat);
   }
 
+  public void setFeatureValue(int addr, int feat, float f) {
+    this.ll_setFloatValue(addr,  feat, f);
+  }
+  
+  public void setFeatureValueNoIndexCorruptionCheck(int addr, int feat, float f) {
+    this.ll_setFloatValueNoIndexCorruptionCheck(addr,  feat,  f);
+  }
+  
   // double
   public void setFeatureValue(int addr, int feat, double s) {
     this.ll_setDoubleValue(addr, feat, s);
@@ -3142,9 +3248,7 @@ public class CASImpl extends AbstractCas
    *                    If <code>type</code> is not a type.
    */
   public int createTempArray(int type, int len) {
-    final int addr = this.getHeap().add(arrayContentOffset + len, type);
-    this.getHeap().heap[(addr + arrayLengthFeatOffset)] = len;
-    return addr;
+    return ll_createArray(type, len);
   }
 
   /*
@@ -3346,47 +3450,140 @@ public class CASImpl extends AbstractCas
   }
   
   /**
-   * Throw an exception if a modification is made to a feature of some FS where:
-   *   - the feature is being used as a key in one or more indexes, and 
-   *   - the FS is known to already have been added to the indexes
+   * This is the method all normal FS feature "setters" call before doing the set operation.
+   * <p style="margin-left:2em">
+   *   The binary deserializers bypass these setters, and directly update the heap values, so they have
+   *   a different impl to avoid index corruption.
+   * <p>
+   * It may do nothing (for performance, it needs to be enabled by a JVM property).
+   * <p>
+   * If enabled, it will check if the update may corrupt any index in any view.  The check tests
+   * whether the feature is being used as a key in one or more indexes and if the FS is in one or more 
+   * corruptable view indices. 
+   * <p>
+   * If true, then:
+   * <ul>
+   *   <li>it may remove and remember (for later adding-back) the FS from all corruptable indices
+   *   (bag indices are not corruptable via updating, so these are skipped). 
+   *   The addback occurs later either via an explicit call to do so, or the end of a protectIndex block, or.
+   *   (if autoIndexProtect is enabled) after the individual feature update is completed.</li>
+   *   <li>it may give a WARN level message to the log. This enables users to 
+   *   implement their own optimized handling of this for "high performance"
+   *   applications which do not want the overhead of runtime checking.  </li></ul>
+   * <p>  
+   *   
    * @param fsRef - the FS to test if it is in the indexes
    * @param featureCode - the feature being tested
    */
   private void checkForInvalidFeatureSetting(int fsRef, int featureCode) {
-    if ((!IS_CHK_FS_UPDATE_CORRUPTS_INDEX) || 
-        (svd.cache_not_in_index == fsRef)) {
+    final int ssz = svd.fssTobeAddedback.size();
+    // skip if protection is disabled, an no explicit protection block
+    if (!IS_PROTECT_INDICES && ssz == 0) {
       return;
     }
-    if (svd.featureCodesInIndexKeys.contains(featureCode)) {
-      if (isFsInCorruptableIndexAnyView(fsRef)) {
-        // signal error - modifying a feature which is used in some index as a key
-        CASRuntimeException e = new CASRuntimeException(CASRuntimeException.ILLEGAL_FEAT_SET,
-            new String[] { 
-              this.getTypeSystemImpl().ll_getFeatureForCode(featureCode).getName(),
-              new FeatureStructureImplC(this, fsRef).toString()});
-           throw e; 
-      } else {
-        svd.cache_not_in_index = fsRef;
-      }
+       
+    // next method skips if the fsRef is not in the index (cache)
+    final boolean wasRemoved = removeFromCorruptableIndexAnyView(
+        fsRef, 
+        (ssz > 0) ? svd.fssTobeAddedback.get(ssz - 1) : 
+                    svd.fsTobeAddedbackSingle, 
+        featureCode);            
+    
+    
+    // skip message if wasn't removed
+    // skip message if protected in explicit block
+    if (wasRemoved && IS_REPORT_FS_UPDATE_CORRUPTS_INDEX && ssz == 0) {
+      // prepare a message which includes the feature which is a key, the fs, and
+      // the call stack.
+      StringWriter sw = new StringWriter();
+      PrintWriter pw = new PrintWriter(sw);
+      new Throwable().printStackTrace(pw);
+      pw.close();
+      String msg = String.format(
+          "While FS was in the index, the feature \"%s\""
+          + ", which is used as a key in one or more indices, "
+          + "was modified\n FS = \"%s\"\n%s%n",
+          this.getTypeSystemImpl().ll_getFeatureForCode(featureCode).getName(),
+          new FeatureStructureImplC(this, fsRef).toString(),  
+          sw.toString());        
+      UIMAFramework.getLogger().log(Level.WARNING, msg);
     }
   }
   
-  private boolean isFsInCorruptableIndexAnyView(final int fsRef) {
-    final int typeCode = getTypeCode(fsRef);
-    final TypeSystemImpl tsi = getTypeSystemImpl();
-    if (tsi.isAnnotationBaseOrSubtype(typeCode)) {
-      // only need to check one view
-      return ll_getSofaCasView(fsRef).indexRepository.isInSetOrSortedIndexInThisView(fsRef); 
+  /**
+   * Do the individual feat update addback if
+   *   a) not in a block mode, and 
+   *   b) running with auto protect indices
+   *   c) not in block-single mode
+   *   
+   * if running in block mode, the add back is delayed until the end of the block
+   *   
+   * @param fsRef the fs to add back
+   */
+  private void maybeAddback(int fsRef) {
+    if (!svd.fsTobeAddedbackSingleInUse && IS_PROTECT_INDICES && svd.fssTobeAddedback.size() == 0) {
+      svd.fsTobeAddedbackSingle.addback(fsRef);
     }
-    // not a subtype of AnnotationBase, need to check all views (except base)
-    final Iterator<CAS> viewIterator = getViewIterator();
-    while (viewIterator.hasNext()) {
-      final CAS view =  viewIterator.next();
-      if (((FSIndexRepositoryImpl)view.getIndexRepository()).isInSetOrSortedIndexInThisView(fsRef)) {
-        return true;
-      }
+  }
+  
+  // next two methods dropped - rather than seeing if something is in the index, and then 
+  // later removing it (two lookups), we just conditionally remove it
+  
+//  /**
+//   * test if this feature is in a key and the FS is in the index
+//   *   side effect - if the fsRef is determined not to be in the index, this is remembered.
+//   * @param fsRef - a feature structure which may be in the index
+//   * @param featCode the feature to test if it is in any key
+//   * @return true if the fsRef is in the index, and the featureCode is used as a key
+//   */
+//  boolean isFeatureAKeyInIndexedFS(int fsRef, int featCode) {
+//    if (svd.cache_not_in_index == fsRef) {  // keep, needed for bin compr deser calls
+//      return false;
+//    }
+//    if (svd.featureCodesInIndexKeys.contains(featCode)) {
+//      if (isFsInCorruptableIndexAnyView(fsRef)) {
+//        return true;
+//      }
+//      svd.cache_not_in_index = fsRef;
+//      return false;
+//    }
+//    return false;
+//  }
+//  
+//  private boolean isFsInCorruptableIndexAnyView(final int fsRef) {
+//    final int typeCode = getTypeCode(fsRef);
+//    final TypeSystemImpl tsi = getTypeSystemImpl();
+//    if (tsi.isAnnotationBaseOrSubtype(typeCode)) {
+//      // only need to check one view
+//      return ll_getSofaCasView(fsRef).indexRepository.isInSetOrSortedIndexInThisView(fsRef); 
+//    }
+//    // not a subtype of AnnotationBase, need to check all views (except base)
+//    final Iterator<CAS> viewIterator = getViewIterator();
+//    while (viewIterator.hasNext()) {
+//      final CAS view =  viewIterator.next();
+//      if (((FSIndexRepositoryImpl)view.getIndexRepository()).isInSetOrSortedIndexInThisView(fsRef)) {
+//        return true;
+//      }
+//    }
+//    return false;  // not in any view's indexes
+//  }
+  
+  /**
+   * A conditional remove, depends on the featCode being used as a key
+   * Skip tests if the FS is known not to be in the indices in any view
+   *
+   * @param fsRef the fs
+   * @param toBeAdded the place to record removal actions
+   * @param featCode the feature code to test if it's used as a key in some index
+   * @return true if the fs was removed
+   */
+  boolean removeFromCorruptableIndexAnyView(final int fsRef, FSsTobeAddedback toBeAdded, int featCode) {
+    if (fsRef != svd.cache_not_in_index && svd.featureCodesInIndexKeys.contains(featCode)) {
+      boolean wasRemoved = removeFromCorruptableIndexAnyView(fsRef, toBeAdded);
+      svd.cache_not_in_index = fsRef; // because will remove it if its in the index.
+      return wasRemoved;
     }
-    return false;  // not in any view's indexes
+    return false;
   }
   
   /**
@@ -3394,80 +3591,63 @@ public class CASImpl extends AbstractCas
    * per view whether it was actually in the index.
    * @param fsRef -
    * @param toBeAdded -
+   * @return true if it was removed (one or more times)
    */
-  void removeFromCorruptableIndexAnyView(final int fsRef, IndexRepoTodos toBeAdded) {
+  boolean removeFromCorruptableIndexAnyView(final int fsRef, FSsTobeAddedback toBeAdded) {
     final int typeCode = getTypeCode(fsRef);
     final TypeSystemImpl tsi = getTypeSystemImpl();
     if (tsi.isAnnotationBaseOrSubtype(typeCode)) {
       // only need to check one view
-      FSIndexRepositoryImpl ir = ll_getSofaCasView(fsRef).indexRepository;
-      if (ir.removeIfInCorrputableIndexInThisView(fsRef)) {
-        toBeAdded.addTodo(ir, fsRef);
-      }
-      return;
+      return removeAndRecord(fsRef, ll_getSofaCasView(fsRef).indexRepository, toBeAdded);
     }
     
     // not a subtype of AnnotationBase, need to check all views (except base)
-    // sofas indexed in the base view are not corruptable. 
+    // sofas indexed in the base view are not corruptable.
     final Iterator<CAS> viewIterator = getViewIterator();
+    boolean wasRemoved = false;
     while (viewIterator.hasNext()) {
-      FSIndexRepositoryImpl repo =  (FSIndexRepositoryImpl) viewIterator.next().getIndexRepository();
-      if (repo.removeIfInCorrputableIndexInThisView(fsRef)) {
-        toBeAdded.addTodo(repo, fsRef);
-      }
+      wasRemoved |= removeAndRecord(fsRef, (FSIndexRepositoryImpl) viewIterator.next().getIndexRepository(), toBeAdded);
     }
+    return wasRemoved;
   }
   
-  /**
-   * Remove the fsRef from any corruptable index in any view, and remember
-   * per view whether it was actually in the index.
-   * @param fsRef -
-   * @param todoRepos -
-   */
-  void removeFromCorruptableIndexAnyView(final int fsRef, List<FSIndexRepositoryImpl> todoRepos) {
-    todoRepos.clear();
-    final int typeCode = getTypeCode(fsRef);
-    final TypeSystemImpl tsi = getTypeSystemImpl();
-    if (tsi.isAnnotationBaseOrSubtype(typeCode)) {
-      // only need to check one view
-      FSIndexRepositoryImpl ir = ll_getSofaCasView(fsRef).indexRepository;
-      if (ir.removeIfInCorrputableIndexInThisView(fsRef)) {
-        todoRepos.add(ir);
-      }
-      return;
+  boolean removeFromCorruptableIndexAnyViewSetCache(final int fsRef, FSsTobeAddedback toBeAdded) {
+    if (fsRef != svd.cache_not_in_index) {
+      svd.cache_not_in_index = fsRef;
+      return removeFromCorruptableIndexAnyView(fsRef, toBeAdded);
     }
-    
-    // not a subtype of AnnotationBase, need to check all views (except base)
-    // sofas indexed in the base view are not corruptable. 
-    final Iterator<CAS> viewIterator = getViewIterator();
-    while (viewIterator.hasNext()) {
-      FSIndexRepositoryImpl repo =  (FSIndexRepositoryImpl) viewIterator.next().getIndexRepository();
-      if (repo.removeIfInCorrputableIndexInThisView(fsRef)) {
-        todoRepos.add(repo);
-      }
+    return false;
+  }
+  
+  /**
+   * remove a FS from corruptable indices in this view
+   * @param fsRef the fs to be removed
+   * @param ir the view
+   * @param toBeAdded the place to record how many times it was in the index, per view
+   * @return true if it was removed, false if it wasn't in any corruptable index.
+   */
+  private boolean removeAndRecord(int fsRef, FSIndexRepositoryImpl ir, FSsTobeAddedback toBeAdded) {
+    int nbrRemoved = ir.removeIfInCorrputableIndexInThisView(fsRef);
+    if (0 < nbrRemoved) {
+      ((FSsTobeAddedback) toBeAdded).recordRemove(fsRef, ir, nbrRemoved);
     }
+    return (0 < nbrRemoved);
   }
+  
 
   public final void ll_setIntValue(int fsRef, int featureCode, int value) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
-    this.getHeap().heap[fsRef + this.svd.casMetadata.featureOffset[featureCode]] = value;
-    if (this.svd.trackingMark != null) {
-    	this.logFSUpdate(fsRef, fsRef +  this.svd.casMetadata.featureOffset[featureCode],
-    			ModifiedHeap.FSHEAP, 1);
-    }
+    setFeatureValue(fsRef, featureCode, value);
   }
 
   public final void ll_setFloatValue(int fsRef, int featureCode, float value) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
-    this.getHeap().heap[fsRef + this.svd.casMetadata.featureOffset[featureCode]] = float2int(value);
-    if (this.svd.trackingMark != null) {
-    	this.logFSUpdate(fsRef, fsRef +  this.svd.casMetadata.featureOffset[featureCode],
-    			ModifiedHeap.FSHEAP, 1);
-    }
+    setFeatureValue(fsRef, featureCode, float2int(value));
+  }
+  
+  public final void ll_setFloatValueNoIndexCorruptionCheck(int fsRef, int featureCode, float value) {
+    setFeatureValueNoIndexCorruptionCheck(fsRef, featureCode, float2int(value));
   }
 
   public final void ll_setStringValue(int fsRef, int featureCode, String value) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
     if (null != value) {
       final TypeSystemImpl ts = this.svd.casMetadata.ts;
       String[] stringSet = ts.ll_getStringSet(ts.ll_getRangeType(featureCode));
@@ -3482,21 +3662,12 @@ public class CASImpl extends AbstractCas
       }
     }
     final int stringAddr = (value == null) ? NULL : this.getStringHeap().addString(value);
-    final int valueAddr = fsRef + this.svd.casMetadata.featureOffset[featureCode];
-    this.getHeap().heap[valueAddr] = stringAddr;
-    if (this.svd.trackingMark != null) {
-    	this.logFSUpdate(fsRef, fsRef +  this.svd.casMetadata.featureOffset[featureCode],
-    			ModifiedHeap.FSHEAP, 1);
-    }
+    setFeatureValue(fsRef, featureCode, stringAddr);
   }
 
   public final void ll_setRefValue(int fsRef, int featureCode, int value) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
-    this.getHeap().heap[fsRef + this.svd.casMetadata.featureOffset[featureCode]] = value;
-    if (this.svd.trackingMark != null) {
-    	this.logFSUpdate(fsRef, fsRef +  this.svd.casMetadata.featureOffset[featureCode],
-    			ModifiedHeap.FSHEAP, 1);
-    }
+    // no index check because refs can't be keys
+    setFeatureValueNoIndexCorruptionCheck(fsRef, featureCode, value);
   }
 
   public final void ll_setIntValue(int fsRef, int featureCode, int value, boolean doTypeChecks) {
@@ -3530,7 +3701,7 @@ public class CASImpl extends AbstractCas
 
   public final void ll_setCharBufferValue(int fsRef, int featureCode, char[] buffer, int start,
       int length) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
+    // don't do any index check here, done by inner call
     final int stringCode = this.getStringHeap().addCharBuffer(buffer, start, length);
     ll_setIntValue(fsRef, featureCode, stringCode);
   }
@@ -3967,12 +4138,8 @@ public class CASImpl extends AbstractCas
   }
 
   public void ll_setBooleanValue(int fsRef, int featureCode, boolean value) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
-    this.getHeap().heap[fsRef + this.svd.casMetadata.featureOffset[featureCode]] = value ? CASImpl.TRUE
-        : CASImpl.FALSE;
-    if (this.svd.trackingMark != null) {
-      this.logFSUpdate(fsRef, fsRef + this.svd.casMetadata.featureOffset[featureCode], ModifiedHeap.FSHEAP, 1);
-    }
+    // no index check because booleans can't be keys
+    setFeatureValueNoIndexCorruptionCheck(fsRef, featureCode, value ? CASImpl.TRUE : CASImpl.FALSE);
   }
 
   public void ll_setBooleanValue(int fsRef, int featureCode, boolean value, boolean doTypeChecks) {
@@ -3983,11 +4150,8 @@ public class CASImpl extends AbstractCas
   }
 
   public final void ll_setByteValue(int fsRef, int featureCode, byte value) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
-    this.getHeap().heap[fsRef + this.svd.casMetadata.featureOffset[featureCode]] = value;
-    if (this.svd.trackingMark != null) {
-      this.logFSUpdate(fsRef, fsRef + this.svd.casMetadata.featureOffset[featureCode], ModifiedHeap.FSHEAP, 1);
-    }
+    // no index check because bytes can't be keys
+    setFeatureValueNoIndexCorruptionCheck(fsRef, featureCode, value);
   }
 
   public void ll_setByteValue(int fsRef, int featureCode, byte value, boolean doTypeChecks) {
@@ -3998,11 +4162,8 @@ public class CASImpl extends AbstractCas
   }
 
   public final void ll_setShortValue(int fsRef, int featureCode, short value) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
-    this.getHeap().heap[fsRef + this.svd.casMetadata.featureOffset[featureCode]] = value;
-    if (this.svd.trackingMark != null) {
-      this.logFSUpdate(fsRef, fsRef + this.svd.casMetadata.featureOffset[featureCode], ModifiedHeap.FSHEAP, 1);
-    }
+    // no index corruption check - shorts not valid as keys
+    setFeatureValueNoIndexCorruptionCheck(fsRef, featureCode, value);
   }
 
   public void ll_setShortValue(int fsRef, int featureCode, short value, boolean doTypeChecks) {
@@ -4013,9 +4174,9 @@ public class CASImpl extends AbstractCas
   }
 
   public void ll_setLongValue(int fsRef, int featureCode, long value) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
+    // no index corruption check - longs not valid as keys
     final int offset = this.getLongHeap().addLong(value);
-    setFeatureValue(fsRef, featureCode, offset);
+    setFeatureValueNoIndexCorruptionCheck(fsRef, featureCode, offset);
   }
 
   public void ll_setLongValue(int fsRef, int featureCode, long value, boolean doTypeChecks) {
@@ -4026,10 +4187,10 @@ public class CASImpl extends AbstractCas
   }
 
   public void ll_setDoubleValue(int fsRef, int featureCode, double value) {
-    checkForInvalidFeatureSetting(fsRef, featureCode);
+    // no index corruption check - doubles not valid as keys
     long val = Double.doubleToLongBits(value);
     final int offset = this.getLongHeap().addLong(val);
-    setFeatureValue(fsRef, featureCode, offset);
+    setFeatureValueNoIndexCorruptionCheck(fsRef, featureCode, offset);
   }
 
   public void ll_setDoubleValue(int fsRef, int featureCode, double value, boolean doTypeChecks) {
@@ -4198,9 +4359,9 @@ public class CASImpl extends AbstractCas
     FeatureStructure fs = createFS(type);
     final int addr = ll_getFSRef(fs);
     // setSofaFeat(addr, this.mySofaRef); // already done by createFS
-    setFeatureValue(addr, ts.startFeatCode, begin);
+    setFeatureValueNotJournaled(addr, ts.startFeatCode, begin); // because it's a create - not in index
     // setStartFeat(addr, begin);
-    setFeatureValue(addr, ts.endFeatCode, end);
+    setFeatureValueNotJournaled(addr, ts.endFeatCode, end); // because it's a create - not in index
     // setEndFeat(addr, end);
     return (AnnotationFS) fs;
   }
@@ -4254,9 +4415,9 @@ public class CASImpl extends AbstractCas
     }
     // Create a new document annotation.
     AnnotationFS doc = createAnnotation(ts.docType, 0, length);
-    getIndexRepository().addFS(doc);
     // Set the language feature to the default value.
     doc.setStringValue(ts.langFeat, CAS.DEFAULT_LANGUAGE_NAME);
+    getIndexRepository().addFS(doc);
     return doc;
   }
 
@@ -4270,10 +4431,28 @@ public class CASImpl extends AbstractCas
     final Feature sofaString = SofaType.getFeatureByBaseName(FEATURE_BASE_NAME_SOFASTRING);
     String newDoc = getSofa(this.mySofaRef).getStringValue(sofaString);
     if (null != newDoc) {
-      getDocumentAnnotation().setIntValue(getEndFeature(), newDoc.length());
+      getDocumentAnnotation(newDoc.length());
     }
   }
 
+  private AnnotationFS getDocumentAnnotation(int length) {
+    if (this == this.svd.baseCAS) {
+      // base CAS has no document
+      return null;
+    }
+    FSIterator<AnnotationFS> it = getAnnotationIndex(this.svd.casMetadata.ts.docType).iterator();
+    if (it.isValid()) {
+      AnnotationFS doc = it.get();
+      if (doc instanceof Annotation) {
+        ((Annotation)doc).setEnd(length);
+      } else {
+        setFeatureValue(((AnnotationImpl)doc).addr, getTypeSystemImpl().endFeatCode, length);
+      }
+      return doc;
+    }
+    return createDocumentAnnotation(length);
+  }
+  
   public AnnotationFS getDocumentAnnotation() {
     if (this == this.svd.baseCAS) {
       // base CAS has no document
@@ -4596,6 +4775,76 @@ public class CASImpl extends AbstractCas
   public final boolean doUseJcasCache() {
     return this.isUsedJcasCache;
   }
+  
+  /**
+   * protectIndices
+   * 
+   * Within the scope of protectIndices, 
+   *   feature updates are checked, and if found to be a key, and the FS is in a corruptable index,
+   *     then the FS is removed from the indices (in all necessary views) (perhaps multiple times
+   *     if the FS was added to the indices multiple times), and this removal is recorded on
+   *     an new instance of FSsTobeReindexed appended to fssTobeAddedback.
+   *     
+   *   Later, when the protectIndices is closed, the tobe items are added back to the indies.
+   */
+  @Override
+  public AutoCloseable protectIndices() {
+    FSsTobeAddedback r = FSsTobeAddedback.createMultiple();
+    svd.fssTobeAddedback.add(r);
+    return r;
+  }
+  
+  /**
+   * This design is to support normal operations where the
+   *   addbacks could be nested
+   * It also handles cases where nested ones were inadvertently left open
+   * Three cases:
+   *    1) the addbacks are the last element in the stack
+   *         - remove it from the stack
+   *    2) the addbacks are (no longer) in the list
+   *         - leave stack alone
+   *    3) the addbacks are in the list but not at the end
+   *         - remove it and all later ones     
+   *  
+   * If the "withProtectedIndices" approach is used, it guarantees proper 
+   * nesting, but the Runnable can't throw checked exceptions.
+   * 
+   * You can do your own try-finally blocks (or use the try with resources
+   * form in Java 8 to do a similar thing with no restrictions on what the
+   * body can contain.
+   * 
+   * @param addbacks
+   */
+  void addbackModifiedFSs (FSsTobeAddedback addbacks) {
+    final List<FSsTobeAddedback> s =  svd.fssTobeAddedback;
+    if (s.get(s.size() - 1) == addbacks) {
+      s.remove(s.size());
+    } else {
+      int pos = s.indexOf(addbacks);
+      if (pos >= 0) {
+        for (int i = s.size() - 1; i > pos; i--) {
+          s.remove(i);
+          ((FSsTobeAddedback)(s.get(i))).addback();
+        }
+      }      
+    }
+    ((FSsTobeAddedback)addbacks).addback();
+  }
+  
+  /**
+   * 
+   * @param r an inner block of code to be run with 
+   */
+  @Override
+  public void protectIndices(Runnable r) {
+    AutoCloseable addbacks = protectIndices();
+    try {
+      r.run();
+    } finally {
+      addbackModifiedFSs((FSsTobeAddedback) addbacks);
+    }
+  }
+  
 
   /**
    * The current implementation only supports 1 marker call per 
@@ -4643,40 +4892,40 @@ public class CASImpl extends AbstractCas
   }
   
   private void logFSUpdate(int fsaddr, int position, ModifiedHeap whichheap, int howmany) {
-	if (this.svd.trackingMark != null && !this.svd.trackingMark.isNew(fsaddr)) {
-	  //log the FS
-	  int lastModifiedFS = -1;	
-	  if (this.svd.modifiedPreexistingFSs.size() > 0) {
-	    lastModifiedFS =  this.svd.modifiedPreexistingFSs.get(this.svd.modifiedPreexistingFSs.size()-1);
-	  }
-	  //only log if the last one logged is not the same fs.s
-	  if (lastModifiedFS != fsaddr) {
-		this.svd.modifiedPreexistingFSs.add(fsaddr);
-	  }	
-	  //log cells that were updated
-	  switch (whichheap) {  
-		case FSHEAP:
-		  for (int i=0; i < howmany;i++) {
-		    this.svd.modifiedFSHeapCells.add(position+i);
-		  }
-		break;
-		case BYTEHEAP:
-		  for (int i=0; i < howmany;i++) {
-		    this.svd.modifiedByteHeapCells.add(position+i);
-		  }
-		break;
-		case SHORTHEAP:
-		  for (int i=0; i < howmany;i++) {
-		    this.svd.modifiedShortHeapCells.add(position+i);
-		  }
-	    break;
-		case LONGHEAP:
-		  for (int i=0; i < howmany;i++) {
-		    this.svd.modifiedLongHeapCells.add(position+i);
-		  }
-	    break;
-	  }
-	}
+  	if (this.svd.trackingMark != null && !this.svd.trackingMark.isNew(fsaddr)) {
+  	  //log the FS
+  	  int lastModifiedFS = -1;	
+  	  if (this.svd.modifiedPreexistingFSs.size() > 0) {
+  	    lastModifiedFS =  this.svd.modifiedPreexistingFSs.get(this.svd.modifiedPreexistingFSs.size()-1);
+  	  }
+  	  //only log if the last one logged is not the same fs.s
+  	  if (lastModifiedFS != fsaddr) {
+  		this.svd.modifiedPreexistingFSs.add(fsaddr);
+  	  }	
+  	  //log cells that were updated
+  	  switch (whichheap) {  
+    		case FSHEAP:
+    		  for (int i=0; i < howmany;i++) {
+    		    this.svd.modifiedFSHeapCells.add(position+i);
+    		  }
+    		break;
+    		case BYTEHEAP:
+    		  for (int i=0; i < howmany;i++) {
+    		    this.svd.modifiedByteHeapCells.add(position+i);
+    		  }
+    		break;
+    		case SHORTHEAP:
+    		  for (int i=0; i < howmany;i++) {
+    		    this.svd.modifiedShortHeapCells.add(position+i);
+    		  }
+    	    break;
+    		case LONGHEAP:
+    		  for (int i=0; i < howmany;i++) {
+    		    this.svd.modifiedLongHeapCells.add(position+i);
+    		  }
+    	    break;
+    	}
+  	}
   }
   
   // made public https://issues.apache.org/jira/browse/UIMA-2478
@@ -4703,5 +4952,13 @@ public class CASImpl extends AbstractCas
   IntVector getModifiedLongHeapAddrs() {
 		return this.svd.modifiedLongHeapCells;  
   }
+
+  @Override
+  public String toString() {
+    String sofa =  (mySofaRef == -1) ? "_InitialView" :
+            (mySofaRef == 0) ? "no Sofa" :
+                               getSofa(this.mySofaRef).getSofaID();
+    return "CASImpl [view: " + sofa + "]";
+  }
   
 }

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSIndexRepositoryImpl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSIndexRepositoryImpl.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSIndexRepositoryImpl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSIndexRepositoryImpl.java Fri Dec 12 05:14:04 2014
@@ -1810,7 +1810,8 @@ public class FSIndexRepositoryImpl imple
       for (int j = 0; j < jMax; j++) {
         iicp = iv.get(j);
         indStrat = iicp.index.getIndexingStrategy();
-        if (indStrat != FSIndex.SET_INDEX) {
+        if (indStrat != FSIndex.SET_INDEX) {  // don't use SET indices because 
+                                              // they miss some items which have been added to the indices
           anIndex = iicp;
           break;
         }
@@ -1980,6 +1981,10 @@ public class FSIndexRepositoryImpl imple
         noIndexOrOnlySetIndices = indexingStrategy == FSIndex.SET_INDEX;
       }
     }
+    // log even if added back, because remove logs remove, and might want to know it was "reindexed"
+    if (this.cas.getCurrentMark() != null) {
+      logIndexOperation(fsRef, true);
+    }
     
     if (isAddback) { return; }
     
@@ -1996,9 +2001,7 @@ public class FSIndexRepositoryImpl imple
       // which is the last one added
       indexes.get(indexes.size() - 1).index.insert(fsRef);
     }
-    if (this.cas.getCurrentMark() != null) {
-      logIndexOperation(fsRef, true);
-    }
+
     if (!this.isUsed[typeCode]) {
       // mark this index as used
       this.isUsed[typeCode] = true;

Added: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSsTobeAddedback.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSsTobeAddedback.java?rev=1644829&view=auto
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSsTobeAddedback.java (added)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/FSsTobeAddedback.java Fri Dec 12 05:14:04 2014
@@ -0,0 +1,253 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.uima.cas.FSIndexRepository;
+import org.apache.uima.internal.util.IntVector;
+
+
+/**
+ * Record information on what was removed, from which view, and (optionally) how many times.
+ * 
+ * 4 varieties:
+ *   1) for a single FS
+ *      a) without count
+ *      b) with count
+ *   2) for multiple FSs
+ *      a) without count
+ *      b) with count   
+ */
+abstract class FSsTobeAddedback implements AutoCloseable {
+  
+  final static boolean SHOW = true;
+  final static AtomicInteger removes = new AtomicInteger(0);
+  
+  /**
+   * does an add back if needed 
+   */
+  public void close() { addback();}
+
+  protected void logPart(FSIndexRepository view) {
+    System.out.format("%,d tobeReindexed: view: %s", removes.incrementAndGet(), view);
+  }
+  
+  protected void log(FSIndexRepositoryImpl view, int count) {
+    if (SHOW) {
+      logPart(view);
+      System.out.format(",  count = %d%n", count);
+    }
+  }
+  
+  private void logPart(int fsAddr, FSIndexRepositoryImpl view) {
+    log(view);
+    System.out.format(",  fsAddr = %,d", fsAddr);
+  }
+  
+  protected void log(int fsAddr, FSIndexRepositoryImpl view, int count) {
+    if (SHOW) {
+      log(view, fsAddr);
+      System.out.format(",  count = %d%n", count);
+    }
+  }
+
+  protected void log(FSIndexRepositoryImpl view) {
+    if (SHOW) {
+      logPart(view);
+      System.out.println();
+    }
+  }
+  
+  protected void log(int fsAddr, FSIndexRepositoryImpl view) {
+    if (SHOW) {
+      logPart(fsAddr, view);
+      System.out.println();
+    }
+  }
+  
+  void recordRemove(FSIndexRepositoryImpl view)                        {throw new UnsupportedOperationException();}
+  void recordRemove(FSIndexRepositoryImpl view, int count)             {throw new UnsupportedOperationException();}
+  void recordRemove(int fsAddr, FSIndexRepositoryImpl view)            {throw new UnsupportedOperationException();}
+  void recordRemove(int fsAddr, FSIndexRepositoryImpl view, int count) {throw new UnsupportedOperationException();}
+  
+  void addback()                                                       {throw new UnsupportedOperationException();}
+  void addback(int fsAddr)                                             {throw new UnsupportedOperationException();}
+  abstract void clear();
+
+  static class FSsTobeAddedbackSingle extends FSsTobeAddedback {
+    final List<FSIndexRepositoryImpl> views = new ArrayList<FSIndexRepositoryImpl>();
+    
+    @Override
+    void recordRemove(FSIndexRepositoryImpl view) {
+      log(view);
+      views.add(view);
+    }
+    
+    @Override
+    void recordRemove(int fsAddr, FSIndexRepositoryImpl view) {
+      recordRemove(view);
+    }
+    
+    @Override
+    void recordRemove(int fsAddr, FSIndexRepositoryImpl view, int count) {
+      if (count != 1) {
+        throw new RuntimeException("internal error");
+      }
+      recordRemove(view);
+    }
+          
+    @Override
+    void addback(int fsAddr) {
+      for (FSIndexRepositoryImpl ir : views) {
+        ir.ll_addback(fsAddr, 1);
+      }
+      clear();
+    }
+    
+    @Override
+    void clear() {
+      views.clear();     
+//      if (SHOW) removes.set(0);
+    }
+  }
+  
+  static class FSsTobeAddedbackSingleCounts extends FSsTobeAddedbackSingle {
+    final IntVector counts = new IntVector(4);
+    
+    @Override
+    void recordRemove(FSIndexRepositoryImpl view, int count) {
+      log(view, count);
+      views.add(view);
+      counts.add(count);
+    }
+          
+    @Override
+    void addback(int fsAddr) {
+      int i = 0;
+      for (FSIndexRepositoryImpl ir : views) {
+        ir.ll_addback(fsAddr, counts.get(i++));
+      }
+      clear();
+    }
+    
+    @Override
+    void clear() {
+      views.clear();
+      counts.removeAllElementsAdjustSizeDown();
+//      if (SHOW) removes.set(0);
+    }
+
+  }
+
+  static class FSsTobeAddedbackMultiple extends FSsTobeAddedback {
+  
+    // impl note: for support of allow_multiple_add_to_indices, each entry is two List elements:
+    //   the count
+    //   the ref to the view
+    final Map<Integer, List<?>> fss2views = new HashMap<Integer, List<?>>();
+    
+    @Override
+    void recordRemove(int fsAddr, FSIndexRepositoryImpl view) {
+      log(fsAddr, view);
+      @SuppressWarnings("unchecked")
+      List<FSIndexRepositoryImpl> irList = (List<FSIndexRepositoryImpl>) fss2views.get(fsAddr);
+      if (null == irList) {
+        fss2views.put(fsAddr,  irList = new ArrayList<FSIndexRepositoryImpl>());
+      }
+      irList.add(view);
+    }
+          
+    @Override
+    void addback() {
+      for (Entry<Integer, List<?>> e : fss2views.entrySet()) {
+        final int fsAddr = e.getKey();
+        @SuppressWarnings("unchecked")
+        final List<FSIndexRepositoryImpl> views = (List<FSIndexRepositoryImpl>) e.getValue();
+        for (FSIndexRepositoryImpl ir : views) {
+          ir.ll_addback(fsAddr, 1);
+        }
+      }
+      clear();
+    }
+    
+    @Override
+    void clear() {
+      fss2views.clear();
+//      if (SHOW) removes.set(0);
+    }
+  }
+  
+  static class FSsTobeAddedbackMultipleCounts extends FSsTobeAddedbackMultiple {
+     
+    @Override
+    void recordRemove(int fsAddr, FSIndexRepositoryImpl view, int count) {
+      log(fsAddr, view, count);
+      @SuppressWarnings("unchecked")
+      List<Object> countsAndViews = (List<Object>) fss2views.get(fsAddr);
+      if (null == countsAndViews) {
+        fss2views.put(fsAddr,  countsAndViews = new ArrayList<Object>());
+      }
+      countsAndViews.add(count);
+      countsAndViews.add(view);
+    }
+    
+    @Override
+    void addback() {
+      for (Entry<Integer, List<?>> e : fss2views.entrySet()) {
+        final int fsAddr = e.getKey();
+        final List<?> countsAndViews = e.getValue();
+      
+        for (int i = 0; i < countsAndViews.size(); ) {
+          final int count = (Integer) countsAndViews.get(i++);
+          final FSIndexRepositoryImpl view = (FSIndexRepositoryImpl) countsAndViews.get(i++);
+          view.ll_addback(fsAddr, count);
+        }  
+      }
+      clear();
+    }
+    
+    @Override
+    void clear() {
+      fss2views.clear();
+//      if (SHOW) removes.set(0);
+    }
+  }
+  
+  /**
+   * @return an impl of this class
+   */
+  public static FSsTobeAddedback createSingle() {
+    return (FSIndexRepositoryImpl.IS_ALLOW_DUP_ADD_2_INDICES) ?
+        new FSsTobeAddedbackSingleCounts() :
+        new FSsTobeAddedbackSingle();
+  }
+  
+  public static FSsTobeAddedback createMultiple() {
+    return (FSIndexRepositoryImpl.IS_ALLOW_DUP_ADD_2_INDICES) ?
+       new FSsTobeAddedbackMultipleCounts() :
+       new FSsTobeAddedbackMultiple();
+  }
+}
+

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasDeserializer.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasDeserializer.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasDeserializer.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasDeserializer.java Fri Dec 12 05:14:04 2014
@@ -538,16 +538,7 @@ public class XmiCasDeserializer {
 //      		  FSsTobeReindexed modifySafely = casBeingFilled.modifySafely()
 //       		  casBeingFilled.removeFromCorruptableIndexAnyView(addr, toBeAddedBack);
             
-      		  AutoCloseable addbacks = casBeingFilled.protectIndices();
-      		  try {      		  
-         	    readFS(addr, attrs, IS_EXISTING_FS);
-      		  } finally {
-        		  try {
-                addbacks.close();
-              } catch (Exception e) {
-                assert(false); // never happen
-              }
-      		  }
+       	    readFS(addr, attrs, IS_EXISTING_FS);
       		} // otherwise ignore
       	}
       }
@@ -762,6 +753,12 @@ public class XmiCasDeserializer {
       
       this.featsSeen = null;
       
+      // before looping over all features for this FS, remove this FS if in any index.
+      // we do this once, before the feature setting loop, because that loop may set a sofa Ref which is 
+      // invalid (to be fixed up later). But the removal code needs a valid sofa ref.
+      if (!isNewFs) {   
+        casBeingFilled.removeFromCorruptableIndexAnyViewSetCache(currentAddr, casBeingFilled.getAddbackSingle());
+      }
       // loop over all features for this FS
       for (int i = 0; i < attrs.getLength(); i++) {
         attrName = attrs.getQName(i);
@@ -796,6 +793,10 @@ public class XmiCasDeserializer {
         }
       }  // end of all features loop
       
+      if (!isNewFs) {
+        casBeingFilled.addbackSingle(currentAddr);
+      }
+      
       if (sofaTypeCode == typeCode && newFS) {
         // If a Sofa, create CAS view to get new indexRepository
         SofaFS sofa = (SofaFS) casBeingFilled.createFS(fsAddr);
@@ -847,7 +848,7 @@ public class XmiCasDeserializer {
       //only update Sofa data features and mime type feature. skip other features.
       //skip Sofa data features if Sofa data is already set.
       //these features may not be modified.
-      if (sofaTypeCode == casBeingFilled.getHeapValue(fsAddr) && !isNewFS(fsAddr) ) {
+      if (sofaTypeCode == casBeingFilled.getHeapValue(fsAddr) && !isNewFS) {
       	if (featName.equals(CAS.FEATURE_BASE_NAME_SOFAID) || 
       		  featName.equals(CAS.FEATURE_BASE_NAME_SOFANUM))   { 
             return feat.getCode();
@@ -858,7 +859,7 @@ public class XmiCasDeserializer {
       	  if (currVal != 0) 
       	    return feat.getCode();
       	}
-      }	  
+      }	 
       handleFeature(fsAddr, feat.getCode(), featVal);
       return feat.getCode();
     }
@@ -895,6 +896,10 @@ public class XmiCasDeserializer {
           try {
             if (!emptyVal(featVal)) {
               if (featCode == sofaFeatCode) {
+                // Debugging in 12 2014 reveals that this code is never run.
+                // because the featuretype of the sofa feature of an annotation is "fs ref", not "int".
+                // These are "fixed up" later.
+                
                 // special handling for "sofa" feature of annotation. Need to change
                 // it from a sofa reference into a sofa number
                 int sofaXmiId = Integer.parseInt(featVal);
@@ -943,7 +948,7 @@ public class XmiCasDeserializer {
         }
         case LowLevelCAS.TYPE_CLASS_FS: {
           try {
-            if (!emptyVal(featVal)) { this.
+            if (!emptyVal(featVal)) { 
               casBeingFilled.setFeatureValue(addr, featCode, Integer.parseInt(featVal));
             }
           } catch (NumberFormatException e) {
@@ -1899,7 +1904,7 @@ public class XmiCasDeserializer {
    * @param sharedData
    *          data structure used to allow the XmiCasSerializer and XmiCasDeserializer to share
    *          information.
-   * @param mergePoint
+   * @param mergePoint (represented as an xmiId, not an fsAddr)
    *          used to support merging multiple XMI CASes. If the mergePoint is negative, "normal"
    *          deserialization will be done, meaning the target CAS will be reset and the entire XMI
    *          content will be deserialized. If the mergePoint is nonnegative (including 0), the

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasSerializer.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasSerializer.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasSerializer.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiCasSerializer.java Fri Dec 12 05:14:04 2014
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.uima.UIMAFramework;
+import org.apache.uima.UIMARuntimeException;
 import org.apache.uima.UimaContext;
 import org.apache.uima.cas.ByteArrayFS;
 import org.apache.uima.cas.CAS;
@@ -413,7 +414,11 @@ public class XmiCasSerializer {
     try {
       ser.cds.serialize();
     } catch (Exception e) {
-      throw (SAXException) e;
+      if (e instanceof SAXException) {
+        throw (SAXException) e;
+      } else {
+        throw new UIMARuntimeException(e);
+      }
     }  
     contentHandler.endDocument();
   }
@@ -472,7 +477,7 @@ public class XmiCasSerializer {
    * @return the original instance, possibly updated
    */
   public XmiCasSerializer setErrorHandler(ErrorHandler eh) {
-    css.eh = eh;
+    css.errorHandler = eh;
     return this;
   }
   

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiSerializationSharedData.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiSerializationSharedData.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiSerializationSharedData.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/cas/impl/XmiSerializationSharedData.java Fri Dec 12 05:14:04 2014
@@ -127,8 +127,7 @@ public class XmiSerializationSharedData
     if (xmiId == 0) {
       // to be sure we get a unique Id, increment maxXmiId and use that
       xmiId = ++maxXmiId;
-      fsAddrToXmiIdMap.put(fsAddr, xmiId);
-      xmiIdToFsAddrMap.put(xmiId, fsAddr);
+      addIdMapping(fsAddr, xmiId);
     }
     return xmiId;
   }

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/JCas.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/JCas.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/JCas.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/JCas.java Fri Dec 12 05:14:04 2014
@@ -634,4 +634,31 @@ public interface JCas extends AbstractCa
    * @throws CASException -
    */
   Iterator<JCas> getViewIterator(String localViewNamePrefix) throws CASException;
+  
+  // temporarily omitted to enable some testing without updating cas wrappers 
+/**
+ * Call this method to set up a region, 
+ * ended by a {@link FSsTobeAddedback.close()} call on the returned object,
+ * You can use this or the {@link #protectIndices(Runnable)} method to protected
+ * the indices.
+ * <p>
+ * This approach allows arbitrary code between  the protectIndices and the associated close method.
+ * <p>
+ * The close method is best done in a finally block, or using the try-with-resources statement in 
+ * Java 8.
+ * 
+ * @return an object used to record things that need adding back
+ */
+AutoCloseable protectIndices();
+
+/**
+ * Runs the code in the runnable inside a protection block, where any modifications to features
+ * done while in this block will be done in a way to protect any indices which otherwise 
+ * might become corrupted by the update action; the protection is achieved by temporarily
+ * removing the FS (if it is in the indices), before the update happens.
+ * At the end of the block, affected indices have any removed-under-the-covers FSs added back.
+ * @param runnable code to execute while protecting the indices. 
+ */
+void protectIndices(Runnable runnable);
+
 }
\ No newline at end of file

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java?rev=1644829&r1=1644828&r2=1644829&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java Fri Dec 12 05:14:04 2014
@@ -1561,4 +1561,22 @@ public class JCasImpl extends AbstractCa
     }
     return viewList.iterator();
   }
+
+  /* (non-Javadoc)
+   * @see org.apache.uima.jcas.JCas#protectIndices()
+   */
+  @Override
+  public AutoCloseable protectIndices() {
+    return casImpl.protectIndices();
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.uima.jcas.JCas#protectIndices(java.lang.Runnable)
+   */
+  @Override
+  public void protectIndices(Runnable runnable) {
+    casImpl.protectIndices(runnable);  
+  }
+  
+  
 }