class VersionAwareLogEntryReader implements LogEntryReader<SOURCE> {

    @Override
    public LogEntry readLogEntry( SOURCE channel ) throws IOException
    {
        try
        {
            LogPositionMarker positionMarker = new LogPositionMarker();
            long skipped = 0;
            while ( true )
            {
                channel.getCurrentPosition( positionMarker );

                byte versionCode = channel.get();
                byte typeCode = channel.get();

                LogEntryVersion version = null;
                LogEntryParser<LogEntry> entryReader;
                LogEntry entry;
                try
                {
                    version = byVersion( versionCode );
                    entryReader = version.entryParser( typeCode );
                    entry = entryReader.parse( version, channel, positionMarker, commandReaderFactory );
                    if ( entry != null && skipped > 0 )
                    {
                        // Take extra care when reading an entry in a bad section. Just because entry reading
                        // didn't throw exception doesn't mean that it's a sane entry.
                        if ( !logEntryMakesSense( entry ) )
                        {
                            throw new IllegalArgumentException( "Log entry " + entry + " which was read after " +
                                    "a bad section of " + skipped + " bytes was read successfully, but " +
                                    "its contents is unrealistic, so treating as part of bad section" );
                        }
                        invalidLogEntryHandler.bytesSkipped( skipped );
                        skipped = 0;
                    }
                }
                catch ( ReadPastEndException e )
                {   // Make these exceptions slip by straight out to the outer handler
                    throw e;
                }
                catch ( Exception e )
                {   // Tag all other exceptions with log position and other useful information
                    LogPosition position = positionMarker.newPosition();
                    e = withMessage( e, e.getMessage() + ". At position " + position +
                            " and entry version " + version );

                    if ( channelSupportsPositioning( channel ) &&
                            invalidLogEntryHandler.handleInvalidEntry( e, position ) )
                    {
                        ((PositionableChannel)channel).setCurrentPosition( positionMarker.getByteOffset() + 1 );
                        skipped++;
                        continue;
                    }
                    throwIfInstanceOf( e, UnsupportedLogVersionException.class );
                    throw new IOException( e );
                }

                if ( !entryReader.skip() )
                {
                    return entry;
                }
            }
        }
        catch ( ReadPastEndException e )
        {
            return null;
        }
    }

    public  VersionAwareLogEntryReader();
    public  VersionAwareLogEntryReader( CommandReaderFactory commandReaderFactory,
            InvalidLogEntryHandler invalidLogEntryHandler );

}