/**
* Copyright (c) 2002-2012 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.consistency.store;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.neo4j.kernel.IdType;
import org.neo4j.kernel.impl.nioneo.store.AbstractBaseRecord;
import org.neo4j.kernel.impl.nioneo.store.DynamicRecord;
import org.neo4j.kernel.impl.nioneo.store.InvalidRecordException;
import org.neo4j.kernel.impl.nioneo.store.NodeRecord;
import org.neo4j.kernel.impl.nioneo.store.PropertyIndexRecord;
import org.neo4j.kernel.impl.nioneo.store.PropertyRecord;
import org.neo4j.kernel.impl.nioneo.store.RecordStore;
import org.neo4j.kernel.impl.nioneo.store.RelationshipRecord;
import org.neo4j.kernel.impl.nioneo.store.RelationshipTypeRecord;
import org.neo4j.kernel.impl.nioneo.store.WindowPoolStats;
/**
* Not thread safe, intended for single threaded use.
*/
public class DiffRecordStore<R extends AbstractBaseRecord> implements RecordStore<R>, Iterable<Long>
{
private final RecordStore<R> actual;
private final Map<Long, R> diff;
private long highId = -1;
public DiffRecordStore( RecordStore<R> actual )
{
this.actual = actual;
this.diff = new HashMap<Long, R>();
}
@Override
public String toString()
{
return "Diff/" + actual;
}
public void markDirty( long id )
{
if ( !diff.containsKey( id ) ) diff.put( id, null );
}
public boolean isModified( long id )
{
return diff.get( id ) != null;
}
public R forceGetRaw( R record )
{
if ( diff.containsKey( record.getLongId() ) )
{
return actual.forceGetRecord( record.getLongId() );
}
else
{
return record;
}
}
@Override
public R forceGetRaw( long id )
{
return actual.forceGetRecord( id );
}
@Override
public int getRecordHeaderSize()
{
return actual.getRecordHeaderSize();
}
@Override
public int getRecordSize()
{
return actual.getRecordSize();
}
@Override
public String getStorageFileName()
{
return actual.getStorageFileName();
}
@Override
public WindowPoolStats getWindowPoolStats()
{
return actual.getWindowPoolStats();
}
@Override
public long getHighId()
{
return Math.max( highId, actual.getHighId() );
}
public long getRawHighId()
{
return actual.getHighId();
}
@Override
public R getRecord( long id )
{
return getRecord( id, false );
}
@Override
public R forceGetRecord( long id )
{
return getRecord( id, true );
}
private R getRecord( long id, boolean force )
{
R record = diff.get( id );
if ( record == null ) return force ? actual.forceGetRecord( id ) : actual.getRecord( id );
if ( !force && !record.inUse() ) throw new InvalidRecordException( record.getClass().getSimpleName() + "[" + id + "] not in use" );
return record;
}
@Override
public void updateRecord( R record )
{
if ( record.getLongId() > highId ) highId = record.getLongId();
diff.put( record.getLongId(), record );
}
@Override
public void forceUpdateRecord( R record )
{
updateRecord( record );
}
@Override
public void accept( RecordStore.Processor processor, R record )
{
actual.accept( new DispatchProcessor( this, processor ), record );
}
@Override
public Iterator<Long> iterator()
{
return diff.keySet().iterator();
}
@Override
public void close()
{
diff.clear();
actual.close();
}
public R getChangedRecord( long id )
{
return diff.get( id );
}
@SuppressWarnings( "unchecked" )
private static class DispatchProcessor extends RecordStore.Processor
{
private final DiffRecordStore<?> diffStore;
private final RecordStore.Processor processor;
DispatchProcessor( DiffRecordStore<?> diffStore, RecordStore.Processor processor )
{
this.diffStore = diffStore;
this.processor = processor;
}
@Override
public void processNode( RecordStore<NodeRecord> store, NodeRecord node )
{
processor.processNode( (RecordStore<NodeRecord>) diffStore, node );
}
@Override
public void processRelationship( RecordStore<RelationshipRecord> store, RelationshipRecord rel )
{
processor.processRelationship( (RecordStore<RelationshipRecord>) diffStore, rel );
}
@Override
public void processProperty( RecordStore<PropertyRecord> store, PropertyRecord property )
{
processor.processProperty( (RecordStore<PropertyRecord>) diffStore, property );
}
@Override
public void processString( RecordStore<DynamicRecord> store, DynamicRecord string, IdType idType )
{
processor.processString( (RecordStore<DynamicRecord>) diffStore, string, idType );
}
@Override
public void processArray( RecordStore<DynamicRecord> store, DynamicRecord array )
{
processor.processArray( (RecordStore<DynamicRecord>) diffStore, array );
}
@Override
public void processRelationshipType( RecordStore<RelationshipTypeRecord> store, RelationshipTypeRecord record )
{
processor.processRelationshipType( (RecordStore<RelationshipTypeRecord>) diffStore, record );
}
@Override
public void processPropertyIndex( RecordStore<PropertyIndexRecord> store, PropertyIndexRecord record )
{
processor.processPropertyIndex( (RecordStore<PropertyIndexRecord>) diffStore, record );
}
}
}