@Override
  public void updateMapping(Object indexKey, Object oldKey, RegionEntry re, Object oldValue)
      throws IMQException {
    try {

      if (DefaultQuery.testHook != null) {
        DefaultQuery.testHook.doTestHook(
            DefaultQuery.TestHook.SPOTS.BEFORE_ADD_OR_UPDATE_MAPPING_OR_DESERIALIZING_NTH_STREAMINGOPERATION,
            null, null);
      }

      // Check if reverse-map is present.
      if (IndexManager.isObjectModificationInplace()) {
        // If reverse map get the old index key from reverse map.
        if (entryToValuesMap.containsKey(re)) {
          oldKey = entryToValuesMap.get(re);
        }
      } else {
        // Check if the old value and new value same.
        // If they are same, that means the value got updated in place.
        // In the absence of reverse-map find the old index key from
        // forward map.
        if (oldValue != null && oldValue == getTargetObjectInVM(re)) {
          oldKey = getOldKey(indexKey, re);
        }
      }

      // No need to update the map if new and old index key are same.
      if (oldKey != null && oldKey.equals(TypeUtils.indexKeyFor(indexKey))) {
        return;
      }

      boolean retry = false;
      indexKey = TypeUtils.indexKeyFor(indexKey);
      if (indexKey.equals(QueryService.UNDEFINED)) {
        Object targetObject = getTargetObjectForUpdate(re);
        if (Token.isInvalidOrRemoved(targetObject)) {
          if (oldKey != null) {
            basicRemoveMapping(oldKey, re, false);
          }
          return;
        }
      }

      do {
        retry = false;
        Object regionEntries = valueToEntriesMap.putIfAbsent(indexKey, re);
        if (regionEntries == TRANSITIONING_TOKEN) {
          retry = true;
          continue;
        } else if (regionEntries == null) {
          if (indexKey != IndexManager.NULL && indexKey != QueryService.UNDEFINED) {
            internalIndexStats.incNumKeys(1);
          }
          numIndexKeys.incrementAndGet();
        } else if (regionEntries instanceof RegionEntry) {
          IndexElemArray elemArray = new IndexElemArray();
          if (DefaultQuery.testHook != null) {
            DefaultQuery.testHook.doTestHook(
                DefaultQuery.TestHook.SPOTS.BEGIN_TRANSITION_FROM_REGION_ENTRY_TO_ELEMARRAY, null,
                null);
          }
          elemArray.add(regionEntries);
          elemArray.add(re);
          if (!valueToEntriesMap.replace(indexKey, regionEntries, elemArray)) {
            retry = true;
          }
          if (DefaultQuery.testHook != null) {
            DefaultQuery.testHook
                .doTestHook(DefaultQuery.TestHook.SPOTS.TRANSITIONED_FROM_REGION_ENTRY_TO_ELEMARRAY,
                    null, null);
          }
          if (DefaultQuery.testHook != null) {
            DefaultQuery.testHook.doTestHook(
                DefaultQuery.TestHook.SPOTS.COMPLETE_TRANSITION_FROM_REGION_ENTRY_TO_ELEMARRAY,
                null, null);
          }
        } else if (regionEntries instanceof IndexConcurrentHashSet) {
          // This synchronized is for avoiding conflcts with remove of
          // ConcurrentHashSet when set size becomes zero during
          // basicRemoveMapping();
          synchronized (regionEntries) {
            ((IndexConcurrentHashSet) regionEntries).add(re);
          }
          if (regionEntries != valueToEntriesMap.get(indexKey)) {
            retry = true;
          }
        } else {
          IndexElemArray elemArray = (IndexElemArray) regionEntries;
          synchronized (elemArray) {
            int threshold = IndexManager.INDEX_ELEMARRAY_THRESHOLD;
            if (IndexManager.INDEX_ELEMARRAY_THRESHOLD_FOR_TESTING > 0) {
              threshold = IndexManager.INDEX_ELEMARRAY_THRESHOLD_FOR_TESTING;
            }
            if (elemArray.size() >= threshold) {
              IndexConcurrentHashSet set =
                  new IndexConcurrentHashSet(IndexManager.INDEX_ELEMARRAY_THRESHOLD + 20, 0.75f, 1);
              // Replace first so that we are sure that the set is placed in
              // index then we should add old elements in the new set.

              if (DefaultQuery.testHook != null) {
                DefaultQuery.testHook.doTestHook(
                    DefaultQuery.TestHook.SPOTS.BEGIN_TRANSITION_FROM_ELEMARRAY_TO_CONCURRENT_HASH_SET,
                    null, null);
              }
              // on a remove from the elem array, another thread could start and complete its remove
              // at this point, that is why we need to replace before adding the elem array elements
              // once we put this set into the forward map, we know any future removes are either
              // captured
              // by our instance of the elem array, or the remove operations will need to do a
              // retry?
              if (!valueToEntriesMap.replace(indexKey, regionEntries, TRANSITIONING_TOKEN)) {
                retry = true;
              } else {
                if (DefaultQuery.testHook != null) {
                  DefaultQuery.testHook
                      .doTestHook(DefaultQuery.TestHook.SPOTS.TRANSITIONED_FROM_ELEMARRAY_TO_TOKEN,
                          null, null);
                }
                set.add(re);
                set.addAll(elemArray);
                if (!valueToEntriesMap.replace(indexKey, TRANSITIONING_TOKEN, set)) {
                  // This should never happen. If we see this in the log, then something is wrong
                  // with the TRANSITIONING TOKEN and synchronization of changing collection types
                  // we should then just go from RE to CHS and completely remove the Elem Array.
                  region.getCache().getLogger().warning(
                      "Unable to transition from index elem to concurrent hash set.  Index needs to be recreated");
                  throw new IndexMaintenanceException(
                      "Unable to transition from index elem to concurrent hash set.  Index needs to be recreated");
                }
                if (DefaultQuery.testHook != null) {
                  DefaultQuery.testHook.doTestHook(
                      DefaultQuery.TestHook.SPOTS.COMPLETE_TRANSITION_FROM_ELEMARRAY_TO_CONCURRENT_HASH_SET,
                      null, null);
                }
              }
            } else {
              elemArray.add(re);
              if (regionEntries != valueToEntriesMap.get(indexKey)) {
                retry = true;
              }
            }
          }
        }

        // Add to reverse Map with the new value.
        if (!retry) {

          // remove from forward map in case of update
          // oldKey is not null only for an update
          if (oldKey != null) {
            basicRemoveMapping(oldKey, re, false);
          }

          if (IndexManager.isObjectModificationInplace()) {
            entryToValuesMap.put(re, indexKey);
          }
        }
      } while (retry);
    } catch (TypeMismatchException ex) {
      throw new IMQException("Could not add object of type " + indexKey.getClass().getName(), ex);
    }
    internalIndexStats.incNumValues(1);
  }
Map for valueOf(indexedExpression)=>RegionEntries. SortedMap<Object, (RegionEntry |
List<RegionEntry>)>. Package access for unit tests.
geode-core/src/main/java/org/apache/geode/cache/query/internal/index/MemoryIndexStore.java