blob: 3cb3dc50936fd259e3f34b6772c0aa5eeb3ab3fd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.jackrabbit.oak.plugins.document.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
/**
* <code>MergeSortedIterators</code> is a specialized implementation of a
* merge sort of already sorted iterators of some type of comparable elements.
* The input iterators must return the elements in sorted order according to
* the provided Comparator. In addition the sequence of iterators must also
* be sorted in a way that the first element of the next iterator is greater
* than the first element of the previous iterator.
*
* @param <T> the entry type
*/
public abstract class MergeSortedIterators<T> implements Iterator<T> {
private final List<PeekingIterator<T>> iterators = new ArrayList<PeekingIterator<T>>();
private final Comparator<T> comparator;
private T lastPeek;
public MergeSortedIterators(final Comparator<T> comparator) {
this.comparator = comparator;
fetchNextIterator();
}
/**
* @return the next {@link Iterator} or <code>null</code> if there is none.
*/
public abstract Iterator<T> nextIterator();
/**
* Provides details about this iterator
*/
public String description() {
return "";
}
//----------------------------< Iterator >----------------------------------
@Override
public boolean hasNext() {
return !iterators.isEmpty();
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
PeekingIterator<T> it = iterators.get(0);
T next = it.next();
// more elements?
if (it.hasNext()) {
adjustFirst();
} else {
// remove from list of iterators
iterators.remove(0);
}
// fetch next iterator?
if (comparator.compare(next, lastPeek) >= 0) {
fetchNextIterator();
}
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
//----------------------------< internal >----------------------------------
private void fetchNextIterator() {
Iterator<T> it = nextIterator();
if (it != null && it.hasNext()) {
PeekingIterator<T> pIt = Iterators.peekingIterator(it);
if (!iterators.isEmpty()
&& comparator.compare(pIt.peek(), lastPeek) < 0) {
throw new IllegalStateException(description() +
" First element of next iterator (" + pIt.peek() + ")" +
" must be after previous iterator (" + lastPeek + ")");
}
lastPeek = pIt.peek();
iterators.add(pIt);
adjustLast();
}
}
private void adjustFirst() {
// shift first iterator until peeked elements are sorted again
int i = 0;
while (i + 1 < iterators.size()) {
if (comparator.compare(iterators.get(i).peek(), iterators.get(i + 1).peek()) > 0) {
Collections.swap(iterators, i, i + 1);
i++;
} else {
break;
}
}
}
private void adjustLast() {
// shift last until sorted again
int i = iterators.size() - 1;
while (i - 1 >= 0) {
if (comparator.compare(iterators.get(i - 1).peek(), iterators.get(i).peek()) > 0) {
Collections.swap(iterators, i, i - 1);
i--;
} else {
break;
}
}
}
}