@Override
  public void clearFilter() {

    data.clearFilter();

    // announce that a full redraw is necessary
    if (onLoad != null) {
      onLoad.notifyObservers(this, new EventObject(this));
    }
  }
  public void sort(final Comparator<R> comparator) {
    // the 2 different data structures require different API's
    // let's wrap another comparator around to adapt between them.
    data.sort(
        new Comparator<KeyValuePair<Long, R>>() {
          public int compare(KeyValuePair<Long, R> o1, KeyValuePair<Long, R> o2) {
            return comparator.compare(o1.getValue(), o2.getValue());
          }
        });

    if (onLoad != null) {
      onLoad.notifyObservers(this, getEventObject());
    }
  }
  @Override
  public void setFilter(final Filter<R> filter) {
    // do the filter (NOTE: we ignore events from the MixedCollection)
    // so we have to throw our own event manually
    data.setFilter(
        new Filter<KeyValuePair<Long, R>>() {

          public boolean call(KeyValuePair<Long, R> item) throws Exception {
            return filter.call(item.getValue());
          }
        });

    // announce that a full redraw is necessary
    if (onLoad != null) {
      onLoad.notifyObservers(this, new EventObject(this));
    }
  }
  private void parseAndLoad(D data) throws Exception {

    @SuppressWarnings(
        value = "unchecked") // Guaranteed type safe as R is defined as <R extends Record>
    Set<R> records = (Set<R>) reader.read(data); // parse it into records

    synchronized (this) {
      // clear the data first.
      this.data.clear();
      // repopulate.
      // also, dont let anyone else do any adds during this time
      synchronized (records) {
        for (R record : records) {
          silentAdd(record);
        }
      }
    }

    // announce the change
    if (onLoad != null) {
      onLoad.notifyObservers(this, getEventObject());
    }
  }