public void prefetchForMerge(
     W window, Collection<W> mergingWindows, MergingStateAccessor<?, W> state) {
   if (isFinishedSetNeeded()) {
     for (ValueState<?> value : state.accessInEachMergingWindow(FINISHED_BITS_TAG).values()) {
       value.readLater();
     }
   }
   rootTrigger
       .getSpec()
       .prefetchOnMerge(
           contextFactory.createMergingStateAccessor(window, mergingWindows, rootTrigger));
 }
  /** Run the trigger merging logic as part of executing the specified merge. */
  public void onMerge(W window, Timers timers, MergingStateAccessor<?, W> state) throws Exception {
    // Clone so that we can detect changes and so that changes here don't pollute merging.
    FinishedTriggersBitSet finishedSet = readFinishedBits(state.access(FINISHED_BITS_TAG)).copy();

    // And read the finished bits in each merging window.
    ImmutableMap.Builder<W, FinishedTriggers> builder = ImmutableMap.builder();
    for (Map.Entry<W, ValueState<BitSet>> entry :
        state.accessInEachMergingWindow(FINISHED_BITS_TAG).entrySet()) {
      // Don't need to clone these, since the trigger context doesn't allow modification
      builder.put(entry.getKey(), readFinishedBits(entry.getValue()));
      // Clear the underlying finished bits.
      clearFinishedBits(entry.getValue());
    }
    ImmutableMap<W, FinishedTriggers> mergingFinishedSets = builder.build();

    TriggerStateMachine.OnMergeContext mergeContext =
        contextFactory.createOnMergeContext(
            window, timers, rootTrigger, finishedSet, mergingFinishedSets);

    // Run the merge from the trigger
    rootTrigger.invokeOnMerge(mergeContext);

    persistFinishedSet(state, finishedSet);
  }