/**
   * Returns the number of values associated with the given <code>rowKey</code> between the given
   * <code>start</code> and <code>end</code> values.
   */
  public int getCount(R rowKey, C start, C end) {
    // fetch the relevant pair of trees
    final TreePair<C> treePair = getTreePair(rowKey);

    // ensure we found something
    if (treePair == null) throw new UnknownKeyException("unrecognized rowKey: " + rowKey);

    // return the number of values between start and end
    return treePair.getCount(start, end);
  }
  /**
   * This listener maintains a fast set of TreePairs for each unique value found in the
   * ValueSegments of the source list. The TreePairs, in turn, are efficient at answering questions
   * about the number of values which fall in the certain segment of the value's continuum.
   *
   * <p>This listener also rebroadcasts ListEvents as DatasetChangeEvents.
   */
  @Override
  public void listChanged(ListEvent<ValueSegment<C, R>> listChanges) {
    // speed up the case when listChanges describes a total clearing of
    // the data structures, (which occurs when switching between projects)
    if (listChanges.getSourceList().isEmpty()) {
      clear();

    } else {
      while (listChanges.next()) {
        final int type = listChanges.getType();
        final int index = listChanges.getIndex();

        if (type == ListEvent.INSERT) {
          // fetch the segment that was inserted
          final ValueSegment<C, R> segment = listChanges.getSourceList().get(index);
          // fetch the TreePair for the segment's value
          TreePair<C> treePair = getTreePair(segment.getValue());

          // if a TreePair has not been created for the segment's value, create one now
          if (treePair == null) {
            treePair = new TreePair<C>();
            valueToTreePairs.put(segment.getValue(), treePair);
          }

          // insert the segment into the TreePair
          treePair.insert(segment);
          // maintain our copy of the source contents
          sourceCopy.add(index, segment);
          // call into a hook for custom handling of this insert by subclasses
          postInsert(segment);

        } else if (type == ListEvent.UPDATE) {
          // fetch the segments involved
          final ValueSegment<C, R> newSegment = listChanges.getSourceList().get(index);
          final ValueSegment<C, R> oldSegment = sourceCopy.set(index, newSegment);

          // fetch the TreePairs involved
          final TreePair<C> oldTreePair = getTreePair(oldSegment.getValue());
          final TreePair<C> newTreePair = getTreePair(newSegment.getValue());

          // delete the segment from the oldTreePair
          oldTreePair.delete(oldSegment);
          postDelete(oldSegment);

          // add the segment to the newTreePair
          newTreePair.insert(newSegment);
          postInsert(newSegment);

        } else if (type == ListEvent.DELETE) {
          // fetch the segment that was removed
          final ValueSegment<C, R> segment = sourceCopy.remove(index);
          // fetch the TreePair for the segment's value
          final TreePair<C> treePair = getTreePair(segment.getValue());

          // delete the segment from the TreePair
          treePair.delete(segment);
          // call into a hook for custom handling of this delete by subclasses
          postDelete(segment);
        }
      }
    }

    // indicate the dataset contents have changed
    fireDatasetChanged();
  }
 /** Returns the number of values associated with the given <code>rowKey</code>. */
 public int getCount(R rowKey) {
   final TreePair treePair = getTreePair(rowKey);
   return treePair == null ? 0 : treePair.size();
 }