blob: 1696791e94abdbb1bafdbe9deef00cdfc9b09ad2 [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.jena.util.iterator;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.jena.atlas.lib.Closeable;
/**
NiceIterator is the standard base class implementing ExtendedIterator. It provides
the static methods for <code>andThen</code>, <code>filterKeep</code> and
<code>filterDrop</code>; these can be reused from any other class. It defines
equivalent instance methods for descendants and to satisfy ExtendedIterator.
*/
public class NiceIterator<T> implements ExtendedIterator<T>
{
public NiceIterator()
{ super(); }
/**
default close: don't need to do anything.
*/
@Override
public void close()
{ }
/**
default hasNext: no elements, return false.
*/
@Override
public boolean hasNext()
{ return false; }
protected void ensureHasNext()
{ if (hasNext() == false) throw new NoSuchElementException(); }
/**
default next: throw an exception.
*/
@Override
public T next()
{ throw new NoSuchElementException( "empty NiceIterator" ); }
/**
Utility method for this and other (sub)classes: raise the appropriate
"no more elements" exception. I note that we raised the wrong exception
in at least one case ...
@param message the string to include in the exception
@return never - but we have a return type to please the compiler
*/
protected T noElements( String message )
{ throw new NoSuchElementException( message ); }
/**
default remove: we have no elements, so we can't remove any.
*/
@Override
public void remove()
{
throw new UnsupportedOperationException( "remove not supported for this iterator" );
}
/**
Answer the next object, and remove it.
*/
@Override
public T removeNext()
{ T result = next(); remove(); return result; }
/**
concatenate two closable iterators.
*/
public static <T> ExtendedIterator<T> andThen( final Iterator<T> a, final Iterator<? extends T> b )
{
final List<Iterator<? extends T>> pending = new ArrayList<>( 2 );
pending.add( b );
return new NiceIterator<T>()
{
private int index = 0;
private Iterator<? extends T> current = a;
private Iterator<? extends T> removeFrom = null;
boolean hasNext = false;
@Override public boolean hasNext()
{
if (hasNext) return true;
if (current.hasNext()) return hasNext = true;
while (index < pending.size())
{
current = advance();
if(current.hasNext()) return hasNext = true;
}
return false;
}
private Iterator< ? extends T> advance()
{
Iterator< ? extends T> result = pending.get( index );
pending.set( index, null );
index += 1;
return result;
}
@Override public T next()
{
if (!hasNext()) noElements( "concatenation" );
removeFrom = current;
hasNext = false;
return current.next();
}
@Override public void forEachRemaining(Consumer<? super T> action)
{
current.forEachRemaining(action);
while(index < pending.size())
{
current = advance();
current.forEachRemaining(action);
}
}
@Override public void close()
{
close( current );
for (int i = index; i < pending.size(); i += 1) close( pending.get(i) );
pending.clear();
removeFrom = null;
}
@Override public void remove()
{
if (null == removeFrom) throw new IllegalStateException("no calls to next() since last call to remove()");
removeFrom.remove();
removeFrom = null;
}
@Override public <X extends T> ExtendedIterator<T> andThen( Iterator<X> other )
{ pending.add( other );
return this; }
};
}
/**
make a new iterator, which is us then the other chap.
*/
@Override
public <X extends T> ExtendedIterator<T> andThen( Iterator<X> other )
{ return andThen( this, other ); }
/**
make a new iterator, which is our elements that pass the filter
*/
@Override
public FilterIterator<T> filterKeep( Predicate<T> f )
{ return new FilterIterator<>( f, this ); }
/**
make a new iterator, which is our elements that do not pass the filter
*/
@Override
public FilterIterator<T> filterDrop( final Predicate<T> f )
{ return new FilterIterator<>( f.negate(), this ); }
/**
make a new iterator which is the elementwise _map1_ of the base iterator.
*/
@Override
public <U> ExtendedIterator<U> mapWith( Function<T, U> map1 )
{ return new Map1Iterator<>( map1, this ); }
/**
If <code>it</code> is {@link Closeable}, close it.
*/
public static void close( Iterator<?> it )
{ if (it instanceof Closeable) ((Closeable) it).close(); }
/**
* An iterator over no elements.
* @return A class singleton which doesn't iterate.
*/
static public <T> ExtendedIterator<T> emptyIterator()
{ return NullIterator.instance() ; }
/**
Answer a list of the elements in order, consuming this iterator.
*/
@Override
public List<T> toList()
{ return asList( this ); }
/**
Answer a list of the elements in order, consuming this iterator.
*/
@Override
public Set<T> toSet()
{ return asSet( this ); }
/**
Answer a list of the elements of <code>it</code> in order, consuming this iterator.
Canonical implementation of toSet().
*/
public static <T> Set<T> asSet( ExtendedIterator<T> it )
{
Set<T> result = new HashSet<>();
it.forEachRemaining(result::add);
return result;
}
/**
Answer a list of the elements from <code>it</code>, in order, consuming
that iterator. Canonical implementation of toList().
*/
public static <T> List<T> asList( ExtendedIterator<T> it )
{
List<T> result = new ArrayList<>();
it.forEachRemaining(result::add);
return result;
}
}