/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.jcr.query.engine.process;
import java.util.Iterator;
import org.mapdb.Serializer;
import org.modeshape.common.collection.SequentialIterator;
import org.modeshape.jcr.cache.CachedNodeSupplier;
import org.modeshape.jcr.query.BufferManager;
import org.modeshape.jcr.query.BufferManager.DistinctBuffer;
import org.modeshape.jcr.query.NodeSequence;
import org.modeshape.jcr.query.RowExtractors.ExtractFromRow;
import org.modeshape.jcr.query.engine.process.BufferedRows.BufferedRow;
import org.modeshape.jcr.query.model.NullOrder;
/**
* @author Randall Hauch (rhauch@redhat.com)
*/
public class SortingSequence extends BufferingSequence {
private final DistinctBuffer<BufferedRow> rowsWithNullKey;
private final NullOrder nullOrder;
private Iterator<BufferedRow> bufferedRows;
private int batchSize = 0;
@SuppressWarnings( {"unchecked"} )
public SortingSequence( String workspaceName,
NodeSequence delegate,
ExtractFromRow extractor,
BufferManager bufferMgr,
CachedNodeSupplier nodeCache,
boolean pack,
boolean useHeap,
boolean allowDuplicates,
NullOrder nullOrder ) {
super(workspaceName, delegate, extractor, bufferMgr, nodeCache, pack, useHeap, allowDuplicates);
this.nullOrder = nullOrder;
// Create the buffer into which we'll place the rows with null keys ...
Serializer<BufferedRow> rowSerializer = (Serializer<BufferedRow>)BufferedRows.serializer(nodeCache, width);
rowsWithNullKey = bufferMgr.createDistinctBuffer(rowSerializer).keepSize(true).useHeap(useHeap).make();
}
@Override
public long getRowCount() {
if (bufferedRows == null) {
bufferedRows = initialize();
}
return super.rowCount() + rowsWithNullKey.size();
}
@Override
public Batch nextBatch() {
if (bufferedRows == null) {
bufferedRows = initialize();
}
long rowsLeftInBatch = this.rowsLeftInBatch.get();
remainingRowCount.addAndGet(-rowsLeftInBatch);
if (remainingRowCount.get() == 0L) return null;
while (rowsLeftInBatch-- > 0) {
// iterate to skip over the rows which were not processed from this batch
bufferedRows.next();
}
return batchFrom(bufferedRows, batchSize);
}
/**
* Initialize this node sequence only the first time that {@link #nextBatch()} is called.
*
* @return the iterator over the buffered rows in this sequence; may be null if this sequence is empty
*/
protected Iterator<BufferedRow> initialize() {
// Load everthing into the buffer ...
batchSize = loadAll(delegate, extractor, rowsWithNullKey);
remainingRowCount.set(buffer.size() + rowsWithNullKey.size());
// We always return the buffered rows in ascending order of the extracted key ...
if (rowsWithNullKey.isEmpty()) {
return buffer.ascending();
}
// Return the rows with NULL first ...
assert nullOrder != null;
switch (nullOrder) {
case NULLS_FIRST:
return SequentialIterator.create(rowsWithNullKey.iterator(), buffer.ascending());
case NULLS_LAST:
return SequentialIterator.create(buffer.ascending(), rowsWithNullKey.iterator());
}
assert false;
return null;
}
@Override
public void close() {
try {
super.close();
} finally {
rowsWithNullKey.close();
}
}
@Override
public String toString() {
return "(sorting-sequence width=" + width() + " order=" + extractor + " " + delegate + ")";
}
}