/**
* 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.checking.full;
import org.neo4j.consistency.RecordType;
import org.neo4j.consistency.checking.ComparativeRecordChecker;
import org.neo4j.consistency.report.ConsistencyReport;
import org.neo4j.consistency.report.PendingReferenceCheck;
import org.neo4j.consistency.store.RecordAccess;
import org.neo4j.consistency.store.RecordReference;
import org.neo4j.kernel.impl.nioneo.store.AbstractBaseRecord;
import org.neo4j.kernel.impl.nioneo.store.AbstractNameRecord;
import org.neo4j.kernel.impl.nioneo.store.DynamicRecord;
import org.neo4j.kernel.impl.nioneo.store.PropertyIndexRecord;
import org.neo4j.kernel.impl.nioneo.store.PropertyRecord;
import org.neo4j.kernel.impl.nioneo.store.RelationshipTypeRecord;
import static org.neo4j.consistency.store.RecordReference.SkippingReference.skipReference;
abstract class DynamicOwner<RECORD extends AbstractBaseRecord> implements Owner
{
static final ComparativeRecordChecker<DynamicRecord, AbstractBaseRecord, ConsistencyReport.DynamicConsistencyReport>
ORPHAN_CHECK =
new ComparativeRecordChecker<DynamicRecord, AbstractBaseRecord, ConsistencyReport.DynamicConsistencyReport>()
{
@Override
public void checkReference( DynamicRecord record, AbstractBaseRecord ignored,
ConsistencyReport.DynamicConsistencyReport report, RecordAccess records )
{
report.orphanDynamicRecord();
}
};
abstract RecordReference<RECORD> record( RecordAccess records );
@Override
public void checkOrphanage()
{
// default: do nothing
}
static class Property extends DynamicOwner<PropertyRecord>
implements ComparativeRecordChecker<PropertyRecord, AbstractBaseRecord, ConsistencyReport.PropertyConsistencyReport>
{
private final long id;
private final RecordType type;
Property( RecordType type, PropertyRecord record )
{
this.type = type;
this.id = record.getId();
}
@Override
RecordReference<PropertyRecord> record( RecordAccess records )
{
return records.property( id );
}
@Override
public void checkReference( PropertyRecord property, AbstractBaseRecord record,
ConsistencyReport.PropertyConsistencyReport report, RecordAccess records )
{
if ( record instanceof PropertyRecord )
{
if ( type == RecordType.STRING_PROPERTY )
{
report.stringMultipleOwners( (PropertyRecord) record );
}
else
{
report.arrayMultipleOwners( (PropertyRecord) record );
}
}
else if ( record instanceof DynamicRecord )
{
if ( type == RecordType.STRING_PROPERTY )
{
report.stringMultipleOwners( (DynamicRecord) record );
}
else
{
report.arrayMultipleOwners( (DynamicRecord) record );
}
}
}
}
static class Dynamic extends DynamicOwner<DynamicRecord>
implements ComparativeRecordChecker<DynamicRecord, AbstractBaseRecord, ConsistencyReport.DynamicConsistencyReport>
{
private final long id;
private final RecordType type;
Dynamic( RecordType type, DynamicRecord record )
{
this.type = type;
this.id = record.getId();
}
@Override
RecordReference<DynamicRecord> record( RecordAccess records )
{
switch ( type )
{
case STRING_PROPERTY:
return records.string( id );
case ARRAY_PROPERTY:
return records.array( id );
case PROPERTY_KEY_NAME:
return records.propertyKeyName( (int)id );
case RELATIONSHIP_LABEL_NAME:
return records.relationshipLabelName( (int)id );
default:
return skipReference();
}
}
@Override
public void checkReference( DynamicRecord block, AbstractBaseRecord record,
ConsistencyReport.DynamicConsistencyReport report, RecordAccess records )
{
if ( record instanceof PropertyRecord )
{
report.nextMultipleOwners( (PropertyRecord) record );
}
else if ( record instanceof DynamicRecord )
{
report.nextMultipleOwners( (DynamicRecord) record );
}
else if ( record instanceof RelationshipTypeRecord )
{
report.nextMultipleOwners( (RelationshipTypeRecord) record );
}
else if ( record instanceof PropertyIndexRecord )
{
report.nextMultipleOwners( (PropertyIndexRecord) record );
}
}
}
static abstract class NameOwner<RECORD extends AbstractNameRecord, REPORT extends ConsistencyReport.NameConsistencyReport<RECORD, REPORT>> extends DynamicOwner<RECORD>
implements ComparativeRecordChecker<RECORD, AbstractBaseRecord, REPORT>
{
@Override
public void checkReference( RECORD name, AbstractBaseRecord record, REPORT genericReport, RecordAccess records )
{
ConsistencyReport.NameConsistencyReport report = genericReport;
if ( record instanceof RelationshipTypeRecord )
{
((ConsistencyReport.LabelConsistencyReport) report)
.nameMultipleOwners( (RelationshipTypeRecord) record );
}
else if ( record instanceof PropertyIndexRecord )
{
((ConsistencyReport.PropertyKeyConsistencyReport) report)
.nameMultipleOwners( (PropertyIndexRecord) record );
}
else if ( record instanceof DynamicRecord )
{
report.nameMultipleOwners( (DynamicRecord) record );
}
}
}
static class PropertyKey extends NameOwner<PropertyIndexRecord, ConsistencyReport.PropertyKeyConsistencyReport>
{
private final int id;
PropertyKey( PropertyIndexRecord record )
{
this.id = record.getId();
}
@Override
RecordReference<PropertyIndexRecord> record( RecordAccess records )
{
return records.propertyKey( id );
}
}
static class RelationshipLabel extends NameOwner<RelationshipTypeRecord,ConsistencyReport.LabelConsistencyReport>
{
private final int id;
RelationshipLabel( RelationshipTypeRecord record )
{
this.id = record.getId();
}
@Override
RecordReference<RelationshipTypeRecord> record( RecordAccess records )
{
return records.relationshipLabel( id );
}
}
static class Unknown extends DynamicOwner<AbstractBaseRecord> implements RecordReference<AbstractBaseRecord>
{
private PendingReferenceCheck<AbstractBaseRecord> reporter;
@Override
RecordReference<AbstractBaseRecord> record( RecordAccess records )
{
// Getting the record for this owner means that some other owner replaced it
// that means that it isn't an orphan, so we skip this orphan check
// and return a record for conflict check that always is ok (by skipping the check)
this.markInCustody();
return skipReference();
}
public void checkOrphanage()
{
PendingReferenceCheck<AbstractBaseRecord> reporter = pop();
if ( reporter != null )
{
reporter.checkReference( null, null );
}
}
void markInCustody()
{
PendingReferenceCheck<AbstractBaseRecord> reporter = pop();
if ( reporter != null )
{
reporter.skip();
}
}
private synchronized PendingReferenceCheck<AbstractBaseRecord> pop()
{
try
{
return this.reporter;
}
finally
{
this.reporter = null;
}
}
@Override
public synchronized void dispatch( PendingReferenceCheck<AbstractBaseRecord> reporter )
{
this.reporter = reporter;
}
}
private DynamicOwner()
{
// only internal subclasses
}
}