/**
   * Constructor
   *
   * @param ssid The state system's ID
   * @param partialInput The state change input object that was used to build the upstream state
   *     system. This partial history will make its own copy (since they have different targets).
   * @param pss The partial history's inner state system. It should already be assigned to
   *     partialInput.
   * @param realBackend The real state history back-end to use. It's supposed to be modular, so it
   *     should be able to be of any type.
   * @param granularity Configuration parameter indicating how many trace events there should be
   *     between each checkpoint
   */
  public PartialHistoryBackend(
      @NonNull String ssid,
      ITmfStateProvider partialInput,
      PartialStateSystem pss,
      IStateHistoryBackend realBackend,
      long granularity) {
    if (granularity <= 0
        || partialInput == null
        || pss == null
        || partialInput.getAssignedStateSystem() != pss) {
      throw new IllegalArgumentException();
    }

    final long startTime = realBackend.getStartTime();

    fSSID = ssid;
    fPartialInput = partialInput;
    fPartialSS = pss;

    fInnerHistory = realBackend;
    fGranularity = granularity;

    fLatestTime = startTime;

    registerCheckpoints();
  }
  @Override
  public void insertPastState(
      long stateStartTime, long stateEndTime, int quark, ITmfStateValue value)
      throws TimeRangeException {
    waitForCheckpoints();

    /* Update the latest time */
    if (stateEndTime > fLatestTime) {
      fLatestTime = stateEndTime;
    }

    /*
     * Check if the interval intersects the previous checkpoint. If so,
     * insert it in the real history back-end.
     *
     * FIXME since intervals are inserted in order of rank, we could avoid
     * doing a map lookup every time here (just compare with the known
     * previous one).
     */
    if (stateStartTime <= fCheckpoints.floorKey(stateEndTime)) {
      fInnerHistory.insertPastState(stateStartTime, stateEndTime, quark, value);
    }
  }
  @Override
  public void doQuery(List<@Nullable ITmfStateInterval> currentStateInfo, long t)
      throws TimeRangeException, StateSystemDisposedException {
    /* Wait for required steps to be done */
    waitForCheckpoints();
    fPartialSS.getUpstreamSS().waitUntilBuilt();

    if (!checkValidTime(t)) {
      throw new TimeRangeException(
          fSSID
              + " Time:"
              + t
              + ", Start:"
              + getStartTime()
              + ", End:"
              + getEndTime()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }

    /* Reload the previous checkpoint */
    long checkpointTime = fCheckpoints.floorKey(t);
    fInnerHistory.doQuery(currentStateInfo, checkpointTime);

    /*
     * Set the initial contents of the partial state system (which is the
     * contents of the query at the checkpoint).
     */
    List<@NonNull ITmfStateInterval> filledStateInfo =
        checkNotNullContents(currentStateInfo.stream()).collect(Collectors.toList());

    fPartialSS.takeQueryLock();
    fPartialSS.replaceOngoingState(filledStateInfo);

    /* Send an event request to update the state system to the target time. */
    TmfTimeRange range =
        new TmfTimeRange(
            /*
             * The state at the checkpoint already includes any state change
             * caused by the event(s) happening exactly at 'checkpointTime',
             * if any. We must not include those events in the query.
             */
            TmfTimestamp.fromNanos(checkpointTime + 1), TmfTimestamp.fromNanos(t));
    ITmfEventRequest request = new PartialStateSystemRequest(fPartialInput, range);
    fPartialInput.getTrace().sendRequest(request);

    try {
      request.waitForCompletion();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    /*
     * Now the partial state system should have the ongoing time we are
     * looking for. However, the method expects a List of *state intervals*,
     * not state values, so we'll create intervals with a dummy end time.
     */
    for (int i = 0; i < currentStateInfo.size(); i++) {
      long start = 0;
      start = ((ITmfStateSystem) fPartialSS).getOngoingStartTime(i);
      ITmfStateValue val = ((ITmfStateSystem) fPartialSS).queryOngoingState(i);

      ITmfStateInterval interval = new TmfStateInterval(start, t, i, checkNotNull(val));
      currentStateInfo.set(i, interval);
    }

    fPartialSS.releaseQueryLock();
  }
 @Override
 public void dispose() {
   fPartialInput.dispose();
   fPartialSS.dispose();
   fInnerHistory.dispose();
 }
 @Override
 public void removeFiles() {
   fInnerHistory.removeFiles();
 }
 @Override
 public long supplyAttributeTreeWriterFilePosition() {
   return fInnerHistory.supplyAttributeTreeWriterFilePosition();
 }
 @Override
 public File supplyAttributeTreeWriterFile() {
   return fInnerHistory.supplyAttributeTreeWriterFile();
 }
 @Override
 public FileInputStream supplyAttributeTreeReader() {
   return fInnerHistory.supplyAttributeTreeReader();
 }
 @Override
 public void finishedBuilding(long endTime) throws TimeRangeException {
   fInnerHistory.finishedBuilding(endTime);
 }
 @Override
 public long getStartTime() {
   return fInnerHistory.getStartTime();
 }