/** @since 3.0 */
  @Override
  public synchronized ITmfContext seekEvent(final ITmfLocation location) {
    // Validate the location
    if (location != null && !(location instanceof TmfExperimentLocation)) {
      return null; // Throw an exception?
    }
    // Make sure we have something to read from
    if (fTraces == null) {
      return null;
    }

    // Initialize the location array if necessary
    TmfLocationArray locationArray =
        ((location == null)
            ? new TmfLocationArray(fTraces.length)
            : ((TmfExperimentLocation) location).getLocationInfo());

    ITmfLocation[] locations = locationArray.getLocations();
    long[] ranks = locationArray.getRanks();

    // Create and populate the context's traces contexts
    final TmfExperimentContext context = new TmfExperimentContext(fTraces.length);

    // Position the traces
    long rank = 0;
    for (int i = 0; i < fTraces.length; i++) {
      // Get the relevant trace attributes
      final ITmfContext traceContext = fTraces[i].seekEvent(locations[i]);
      context.setContext(i, traceContext);
      traceContext.setRank(ranks[i]);
      locations[i] = traceContext.getLocation(); // update location after seek
      context.setEvent(i, fTraces[i].getNext(traceContext));
      rank += ranks[i];
    }

    // Finalize context
    context.setLocation(new TmfExperimentLocation(new TmfLocationArray(locations, ranks)));
    context.setLastTrace(TmfExperimentContext.NO_TRACE);
    context.setRank(rank);

    return context;
  }
  @Override
  public synchronized ITmfEvent getNext(ITmfContext context) {

    // Validate the context
    if (!(context instanceof TmfExperimentContext)) {
      return null; // Throw an exception?
    }

    // Make sure that we have something to read from
    if (fTraces == null) {
      return null;
    }

    TmfExperimentContext expContext = (TmfExperimentContext) context;

    // If an event was consumed previously, first get the next one from that
    // trace
    final int lastTrace = expContext.getLastTrace();
    if (lastTrace != TmfExperimentContext.NO_TRACE) {
      final ITmfContext traceContext = expContext.getContext(lastTrace);
      expContext.setEvent(lastTrace, fTraces[lastTrace].getNext(traceContext));
      expContext.setLastTrace(TmfExperimentContext.NO_TRACE);
    }

    // Scan the candidate events and identify the "next" trace to read from
    int trace = TmfExperimentContext.NO_TRACE;
    ITmfTimestamp timestamp = TmfTimestamp.BIG_CRUNCH;
    for (int i = 0; i < fTraces.length; i++) {
      final ITmfEvent event = expContext.getEvent(i);
      if (event != null && event.getTimestamp() != null) {
        final ITmfTimestamp otherTS = event.getTimestamp();
        if (otherTS.compareTo(timestamp, true) < 0) {
          trace = i;
          timestamp = otherTS;
        }
      }
    }

    ITmfEvent event = null;
    if (trace != TmfExperimentContext.NO_TRACE) {
      event = expContext.getEvent(trace);
      if (event != null) {
        updateAttributes(expContext, event.getTimestamp());
        expContext.increaseRank();
        expContext.setLastTrace(trace);
        final ITmfContext traceContext = expContext.getContext(trace);
        if (traceContext == null) {
          throw new IllegalStateException();
        }

        // Update the experiment location
        TmfLocationArray locationArray =
            new TmfLocationArray(
                ((TmfExperimentLocation) expContext.getLocation()).getLocationInfo(),
                trace,
                traceContext.getLocation(),
                traceContext.getRank());
        expContext.setLocation(new TmfExperimentLocation(locationArray));

        processEvent(event);
      }
    }

    return event;
  }