/*
* Copyright (c) 2002-2009 "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.kernel.impl.core;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.kernel.impl.nioneo.store.PropertyData;
import org.neo4j.kernel.impl.transaction.LockType;
import org.neo4j.kernel.impl.util.ArrayMap;
abstract class Primitive
{
protected final int id;
protected final NodeManager nodeManager;
private ArrayMap<Integer,PropertyData> propertyMap = null;
protected abstract void changeProperty( int propertyId, Object value );
protected abstract int addProperty( PropertyIndex index, Object value );
protected abstract void removeProperty( int propertyId );
protected abstract ArrayMap<Integer,PropertyData> loadProperties(
boolean light );
Primitive( int id, NodeManager nodeManager )
{
this.id = id;
this.nodeManager = nodeManager;
}
public GraphDatabaseService getGraphDatabase()
{
return nodeManager.getGraphDbService();
}
Primitive( int id, boolean newPrimitive, NodeManager nodeManager )
{
this.id = id;
this.nodeManager = nodeManager;
if ( newPrimitive )
{
propertyMap = new ArrayMap<Integer,PropertyData>( 9, false, true );
}
}
public long getId()
{
return this.id;
}
public Iterable<Object> getPropertyValues()
{
ArrayMap<Integer,PropertyData> skipMap =
nodeManager.getCowPropertyRemoveMap( this );
ArrayMap<Integer,PropertyData> addMap =
nodeManager.getCowPropertyAddMap( this );
ensureFullProperties();
List<Object> values = new ArrayList<Object>();
for ( Integer index : propertyMap.keySet() )
{
if ( skipMap != null && skipMap.get( index ) != null )
{
continue;
}
if ( addMap != null && addMap.get( index ) != null )
{
continue;
}
values.add( propertyMap.get( index ).getValue() );
}
if ( addMap != null )
{
for ( PropertyData property : addMap.values() )
{
values.add( property.getValue() );
}
}
return values;
}
public Iterable<String> getPropertyKeys()
{
ArrayMap<Integer,PropertyData> skipMap =
nodeManager.getCowPropertyRemoveMap( this );
ArrayMap<Integer,PropertyData> addMap =
nodeManager.getCowPropertyAddMap( this );
ensureFullProperties();
List<String> keys = new ArrayList<String>();
for ( Integer index : propertyMap.keySet() )
{
if ( skipMap != null && skipMap.get( index ) != null )
{
continue;
}
if ( addMap != null && addMap.get( index ) != null )
{
continue;
}
keys.add( nodeManager.getIndexFor( index ).getKey() );
}
if ( addMap != null )
{
for ( Integer index : addMap.keySet() )
{
keys.add( nodeManager.getIndexFor( index ).getKey() );
}
}
return keys;
}
public Object getProperty( String key ) throws NotFoundException
{
if ( key == null )
{
throw new IllegalArgumentException( "null key" );
}
ArrayMap<Integer,PropertyData> skipMap =
nodeManager.getCowPropertyRemoveMap( this );
ArrayMap<Integer,PropertyData> addMap =
nodeManager.getCowPropertyAddMap( this );
ensureFullProperties();
for ( PropertyIndex index : nodeManager.index( key ) )
{
if ( skipMap != null && skipMap.get( index.getKeyId() ) != null )
{
throw newPropertyNotFoundException( key );
}
if ( addMap != null )
{
PropertyData property = addMap.get( index.getKeyId() );
if ( property != null )
{
return getPropertyValue( property );
}
}
PropertyData property = propertyMap.get( index.getKeyId() );
if ( property != null )
{
return getPropertyValue( property );
}
}
PropertyData property = getSlowProperty( addMap, skipMap, key );
if ( property != null )
{
return getPropertyValue( property );
}
throw newPropertyNotFoundException( key );
}
private NotFoundException newPropertyNotFoundException( String key )
{
return new NotFoundException( key +
" property not found for " + this + "." );
}
private PropertyData getSlowProperty( ArrayMap<Integer,PropertyData> addMap,
ArrayMap<Integer,PropertyData> skipMap, String key )
{
if ( nodeManager.hasAllPropertyIndexes() )
{
return null;
}
if ( addMap != null )
{
for ( int keyId : addMap.keySet() )
{
if ( !nodeManager.hasIndexFor( keyId ) )
{
PropertyIndex indexToCheck =
nodeManager.getIndexFor( keyId );
if ( indexToCheck.getKey().equals( key ) )
{
if ( skipMap != null && skipMap.get( keyId ) != null )
{
throw newPropertyNotFoundException( key );
}
PropertyData property =
addMap.get( indexToCheck.getKeyId() );
if ( property != null )
{
return property;
}
}
}
}
}
for ( int keyId : propertyMap.keySet() )
{
if ( !nodeManager.hasIndexFor( keyId ) )
{
PropertyIndex indexToCheck = nodeManager.getIndexFor( keyId );
if ( indexToCheck.getKey().equals( key ) )
{
if ( skipMap != null && skipMap.get( keyId ) != null )
{
throw newPropertyNotFoundException( key );
}
PropertyData property = propertyMap.get(
indexToCheck.getKeyId() );
if ( property != null )
{
return property;
}
}
}
}
return null;
}
public Object getProperty( String key, Object defaultValue )
{
if ( key == null )
{
throw new IllegalArgumentException( "null key" );
}
ArrayMap<Integer,PropertyData> skipMap =
nodeManager.getCowPropertyRemoveMap( this );
ArrayMap<Integer,PropertyData> addMap =
nodeManager.getCowPropertyAddMap( this );
ensureFullProperties();
for ( PropertyIndex index : nodeManager.index( key ) )
{
if ( skipMap != null && skipMap.get( index.getKeyId() ) != null )
{
return defaultValue;
}
if ( addMap != null )
{
PropertyData property = addMap.get( index.getKeyId() );
if ( property != null )
{
return getPropertyValue( property );
}
}
PropertyData property = propertyMap.get( index.getKeyId() );
if ( property != null )
{
return getPropertyValue( property );
}
}
PropertyData property = getSlowProperty( addMap, skipMap, key );
if ( property != null )
{
return getPropertyValue( property );
}
return defaultValue;
}
public boolean hasProperty( String key )
{
if ( key == null )
{
return false;
}
ArrayMap<Integer,PropertyData> skipMap =
nodeManager.getCowPropertyRemoveMap( this );
ArrayMap<Integer,PropertyData> addMap =
nodeManager.getCowPropertyAddMap( this );
ensureFullProperties();
for ( PropertyIndex index : nodeManager.index( key ) )
{
if ( skipMap != null && skipMap.get( index.getKeyId() ) != null )
{
return false;
}
if ( addMap != null )
{
PropertyData property = addMap.get( index.getKeyId() );
if ( property != null )
{
return true;
}
}
PropertyData property = propertyMap.get( index.getKeyId() );
if ( property != null )
{
return true;
}
}
PropertyData property = getSlowProperty( addMap, skipMap, key );
if ( property != null )
{
return true;
}
return false;
}
public void setProperty( String key, Object value )
{
if ( key == null || value == null )
{
throw new IllegalArgumentException( "Null parameter, " + "key=" +
key + ", " + "value=" + value );
}
nodeManager.acquireLock( this, LockType.WRITE );
boolean success = false;
try
{
ensureFullProperties();
ArrayMap<Integer,PropertyData> addMap =
nodeManager.getCowPropertyAddMap( this, true );
ArrayMap<Integer,PropertyData> skipMap =
nodeManager.getCowPropertyRemoveMap( this );
PropertyIndex index = null;
PropertyData property = null;
boolean foundInSkipMap = false;
for ( PropertyIndex cachedIndex : nodeManager.index( key ) )
{
if ( skipMap != null )
{
if ( skipMap.remove( cachedIndex.getKeyId() ) != null )
{
foundInSkipMap = true;
}
}
index = cachedIndex;
property = addMap.get( cachedIndex.getKeyId() );
if ( property != null )
{
break;
}
property = propertyMap.get( cachedIndex.getKeyId() );
if ( property != null )
{
break;
}
}
if ( property == null && !nodeManager.hasAllPropertyIndexes() )
{
for ( int keyId : addMap.keySet() )
{
if ( !nodeManager.hasIndexFor( keyId ) )
{
PropertyIndex indexToCheck = nodeManager
.getIndexFor( keyId );
if ( indexToCheck.getKey().equals( key ) )
{
if ( skipMap != null )
{
skipMap.remove( indexToCheck.getKeyId() );
}
index = indexToCheck;
property = addMap.get( indexToCheck.getKeyId() );
if ( property != null )
{
break;
}
}
}
}
if ( property == null )
{
for ( int keyId : propertyMap.keySet() )
{
if ( !nodeManager.hasIndexFor( keyId ) )
{
PropertyIndex indexToCheck = nodeManager
.getIndexFor( keyId );
if ( indexToCheck.getKey().equals( key ) )
{
if ( skipMap != null )
{
skipMap.remove( indexToCheck.getKeyId() );
}
index = indexToCheck;
property = propertyMap.get( indexToCheck
.getKeyId() );
if ( property != null )
{
break;
}
}
}
}
}
}
if ( index == null )
{
index = nodeManager.createPropertyIndex( key );
}
if ( property != null && !foundInSkipMap )
{
int propertyId = property.getId();
changeProperty( propertyId, value );
property = new PropertyData( propertyId, value );
}
else
{
int propertyId = addProperty( index, value );
property = new PropertyData( propertyId, value );
}
addMap.put( index.getKeyId(), property );
success = true;
}
finally
{
nodeManager.releaseLock( this, LockType.WRITE );
if ( !success )
{
setRollbackOnly();
}
}
}
public Object removeProperty( String key )
{
if ( key == null )
{
throw new IllegalArgumentException( "Null parameter." );
}
nodeManager.acquireLock( this, LockType.WRITE );
boolean success = false;
try
{
ensureFullProperties();
PropertyData property = null;
ArrayMap<Integer,PropertyData> addMap =
nodeManager.getCowPropertyAddMap( this );
ArrayMap<Integer,PropertyData> removeMap =
nodeManager.getCowPropertyRemoveMap( this, true );
for ( PropertyIndex cachedIndex : nodeManager.index( key ) )
{
if ( addMap != null )
{
property = addMap.remove( cachedIndex.getKeyId() );
if ( property != null )
{
removeMap.put( cachedIndex.getKeyId(), property );
break;
}
}
if ( removeMap.get( cachedIndex.getKeyId() ) != null )
{
success = true;
return null;
}
property = propertyMap.get( cachedIndex.getKeyId() );
if ( property != null )
{
removeMap.put( cachedIndex.getKeyId(), property );
break;
}
}
if ( property == null && !nodeManager.hasAllPropertyIndexes() )
{
if ( addMap != null )
{
for ( int keyId : addMap.keySet() )
{
if ( !nodeManager.hasIndexFor( keyId ) )
{
PropertyIndex indexToCheck = nodeManager
.getIndexFor( keyId );
if ( indexToCheck.getKey().equals( key ) )
{
property = addMap.remove( indexToCheck
.getKeyId() );
if ( property != null )
{
removeMap.put( indexToCheck.getKeyId(),
property );
break;
}
}
}
}
if ( property == null )
{
for ( int keyId : propertyMap.keySet() )
{
if ( !nodeManager.hasIndexFor( keyId ) )
{
PropertyIndex indexToCheck = nodeManager
.getIndexFor( keyId );
if ( indexToCheck.getKey().equals( key ) )
{
property = propertyMap.get( indexToCheck
.getKeyId() );
if ( property != null )
{
removeMap.put( indexToCheck.getKeyId(),
property );
break;
}
}
}
}
}
}
}
if ( property == null )
{
success = true;
return null;
}
removeProperty( property.getId() );
success = true;
return getPropertyValue( property );
}
finally
{
nodeManager.releaseLock( this, LockType.WRITE );
if ( !success )
{
setRollbackOnly();
}
}
}
private Object getPropertyValue( PropertyData property )
{
Object value = property.getValue();
if ( value == null )
{
value = nodeManager.loadPropertyValue( property.getId() );
property.setNewValue( value );
}
return value;
}
protected void commitPropertyMaps(
ArrayMap<Integer,PropertyData> cowPropertyAddMap,
ArrayMap<Integer,PropertyData> cowPropertyRemoveMap )
{
if ( propertyMap == null )
{
// we will load full in some other tx
return;
}
if ( cowPropertyAddMap != null )
{
for ( Integer index : cowPropertyAddMap.keySet() )
{
propertyMap.put( index, cowPropertyAddMap.get( index ) );
}
}
if ( cowPropertyRemoveMap != null && propertyMap != null )
{
for ( Integer index : cowPropertyRemoveMap.keySet() )
{
propertyMap.remove( index );
}
}
}
private boolean ensureFullProperties()
{
if ( propertyMap == null )
{
this.propertyMap = loadProperties( false );
return true;
}
return false;
}
private boolean ensureFullLightProperties()
{
if ( propertyMap == null )
{
this.propertyMap = loadProperties( true );
return true;
}
return false;
}
protected void setRollbackOnly()
{
nodeManager.setRollbackOnly();
}
protected List<PropertyEventData> getAllCommittedProperties()
{
ensureFullLightProperties();
List<PropertyEventData> props =
new ArrayList<PropertyEventData>( propertyMap.size() );
for ( Map.Entry<Integer,PropertyData> entry : propertyMap.entrySet() )
{
PropertyIndex index = nodeManager.getIndexFor( entry.getKey() );
Object value = getPropertyValue( propertyMap.get( index.getKeyId() ) );
props.add( new PropertyEventData( index.getKey(), value ) );
}
return props;
}
protected Object getCommittedPropertyValue( String key )
{
ensureFullLightProperties();
for ( PropertyIndex index : nodeManager.index( key ) )
{
PropertyData property = propertyMap.get( index.getKeyId() );
if ( property != null )
{
return getPropertyValue( property );
}
}
return null;
}
}