/*******************************************************************************
* Copyright 2014 Analog Devices, Inc.
*
* 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 com.analog.lyric.collect;
import java.io.DataInput;
import java.io.IOException;
import java.util.NoSuchElementException;
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
/**
* Partial implementation of {@link PrimitiveIterator} based on a {@link DataInput} object.
*/
@ThreadSafe
public abstract class AbstractPrimitiveDataInputIterator<T> implements PrimitiveIterator<T>
{
/*-------
* State
*/
/**
* The underlying source of primitive values.
*/
protected final DataInput _input;
/**
* Enumerates the current state of the iterator:
* <dl>
* <dt>NOT_READ</dt><dd>Next value not yet read from input source</dd>
* <dt>READ</dt><dd>Next value is stored in a local field</dd>
* <dt>EOS</dt><dd>End of stream - no more values are available</dd>
* </dl>
*/
private static enum ReadState { NOT_READ, READ, EOS }
/**
* The current state of the iterator.
*/
@GuardedBy("this")
private ReadState _state = ReadState.NOT_READ;
/*--------------
* Construction
*/
/**
* Construct based on given underlying {@code input}.
*/
protected AbstractPrimitiveDataInputIterator(DataInput input)
{
_input = input;
}
/*------------------
* Iterator methods
*/
/**
* Indicates if another value is available.
* <p>
* @throws RuntimeException thrown by underlying {@link DataInput} object. Once this throws an exception subsequent
* calls will simply return false.
* <p>
* @return true if {@link #next()} (or its primitive variant)
* will return a value without throwing an exception.
*/
@Override
public synchronized boolean hasNext()
{
boolean result = false;
switch (_state)
{
case NOT_READ:
try
{
_state = ReadState.EOS;
result = readNext();
if (result)
{
_state = ReadState.READ;
}
}
catch (IOException ex)
{
result = false;
}
break;
case READ:
result = true;
break;
case EOS:
result = false;
break;
}
return result;
}
/**
* @throws UnsupportedOperationException
*/
@Override
public void remove()
{
throw new UnsupportedOperationException("PrimitiveIterator.remove");
}
/*--------------------------------------------
* AbstractPrimitiveDataInputIterator methods
*/
/**
* This should be invoked by subclass implementations of {@link #next()}
* or their primitive variant. This will cause the next call to {@link #hasNext()}
* to read next value using {@link #readNext()}.
* <p>
* @throws NoSuchElementException if {@link #hasNext()} returns false.
*/
protected void assertValueWasRead()
{
if (!hasNext())
{
throw new NoSuchElementException();
}
_state = ReadState.NOT_READ;
}
/**
* Implementations should read next value from {@link #_input} into a primitive field of
* the correct type.
* <p>
* @throws IOException
*/
protected abstract boolean readNext() throws IOException;
}