@SuppressWarnings("unchecked")
  protected BufferingSequence(
      String workspaceName,
      NodeSequence delegate,
      ExtractFromRow extractor,
      BufferManager bufferMgr,
      CachedNodeSupplier nodeCache,
      boolean pack,
      boolean useHeap,
      boolean allowDuplicates) {
    super(delegate);
    assert extractor != null;
    this.workspaceName = workspaceName;
    this.width = delegate.width();
    this.cache = nodeCache;
    this.extractor = extractor;

    // Set up the row factory based upon the width of the delegate sequence...
    this.rowFactory = BufferedRows.serializer(nodeCache, width);

    // Set up the buffer ...
    SortingBuffer<Object, BufferedRow> buffer = null;
    TypeFactory<?> keyType = extractor.getType();
    if (allowDuplicates) {
      @SuppressWarnings("rawtypes")
      Serializer<? extends Comparable> keySerializer =
          (Serializer<? extends Comparable<?>>) bufferMgr.serializerFor(keyType);
      buffer =
          bufferMgr
              .createSortingWithDuplicatesBuffer(
                  keySerializer,
                  extractor.getType().getComparator(),
                  (BufferedRowFactory<BufferedRow>) rowFactory)
              .keepSize(true)
              .useHeap(useHeap)
              .make();
    } else {
      BTreeKeySerializer<Object> keySerializer =
          (BTreeKeySerializer<Object>) bufferMgr.bTreeKeySerializerFor(keyType, pack);
      if (keySerializer instanceof KeySerializerWithComparator) {
        keySerializer =
            ((KeySerializerWithComparator<Object>) keySerializer)
                .withComparator(extractor.getType().getComparator());
      }
      buffer =
          bufferMgr
              .createSortingBuffer(keySerializer, (BufferedRowFactory<BufferedRow>) rowFactory)
              .keepSize(true)
              .useHeap(useHeap)
              .make();
    }
    this.buffer = buffer;
  }
 /**
  * Load all of the rows from the supplied sequence into the buffer.
  *
  * @param sequence the node sequence; may not be null
  * @param extractor the extractor for the sortable value; may not be null
  * @param rowsWithNullKey the buffer into which should be placed all rows for which the extracted
  *     key value is null; may be null if these are not to be kept
  * @return the size of the first batch, or 0 if there are no rows found
  */
 protected int loadAll(
     NodeSequence sequence,
     ExtractFromRow extractor,
     DistinctBuffer<BufferedRow> rowsWithNullKey) {
   // Put all of the batches from the sequence into the buffer
   Batch batch = sequence.nextBatch();
   int batchSize = 0;
   Object value = null;
   while (batch != null && batchSize == 0) {
     while (batch.hasNext()) {
       batch.nextRow();
       value = extractor.getValueInRow(batch);
       if (value instanceof Object[]) {
         // Put each of the values in the buffer ...
         for (Object v : (Object[]) value) {
           buffer.put(v, createRow(batch));
         }
       } else if (value != null) {
         buffer.put(value, createRow(batch));
       } else if (rowsWithNullKey != null) {
         rowsWithNullKey.addIfAbsent(createRow(batch));
       }
       ++batchSize;
     }
     batch = sequence.nextBatch();
   }
   while (batch != null) {
     while (batch.hasNext()) {
       batch.nextRow();
       value = extractor.getValueInRow(batch);
       if (value instanceof Object[]) {
         // Put each of the values in the buffer ...
         for (Object v : (Object[]) value) {
           buffer.put(v, createRow(batch));
         }
       } else if (value != null) {
         buffer.put(value, createRow(batch));
       } else if (rowsWithNullKey != null) {
         rowsWithNullKey.addIfAbsent(createRow(batch));
       }
     }
     batch = sequence.nextBatch();
   }
   return batchSize;
 }