package org.neo4j.rdf.store.representation.standard;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.index.IndexService;
import org.neo4j.index.lucene.LuceneIndexService;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.neo4j.kernel.impl.transaction.LockManager;
import org.neo4j.meta.model.MetaModel;
import org.neo4j.rdf.fulltext.FulltextIndex;
import org.neo4j.rdf.model.Literal;
import org.neo4j.rdf.model.Uri;
import org.neo4j.rdf.store.representation.AbstractElement;
import org.neo4j.rdf.store.representation.AbstractNode;
import org.neo4j.rdf.store.representation.AbstractRelationship;
import org.neo4j.rdf.store.representation.AbstractRepresentation;
import org.neo4j.rdf.store.representation.RepresentationExecutor;
import org.neo4j.util.GraphDatabaseUtil;
import org.neo4j.util.PropertyArraySet;
public abstract class AbstractUriBasedExecutor implements
RepresentationExecutor
{
public static final String START_OF_ILLEGAL_URI = "%";
public static final int DEFAULT_URI_CACHE_SIZE = 100000;
public static final String URI_PROPERTY_KEY = "uri";
public static final String LITERAL_VALUE_KEY = "value";
public static final String META_EXECUTOR_INFO_KEY = "meta";
public static final String RDF_NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
public static final String RDF_TYPE_URI = RDF_NAMESPACE + "type";
private final GraphDatabaseService graphDb;
private final GraphDatabaseUtil graphDbUtil;
private final IndexService index;
private final MetaModel model;
private FulltextIndex fulltextIndex;
public AbstractUriBasedExecutor( GraphDatabaseService graphDb,
IndexService index, MetaModel optionalModel,
FulltextIndex optionalFulltextIndex )
{
this.graphDb = graphDb;
this.index = index;
this.graphDbUtil = new GraphDatabaseUtil( graphDb );
this.model = optionalModel;
this.fulltextIndex = optionalFulltextIndex;
if ( this.index instanceof LuceneIndexService )
{
LuceneIndexService luceneIndex = (LuceneIndexService) this.index;
Integer cacheSize = luceneIndex.getEnabledCacheSize( URI_PROPERTY_KEY );
if ( cacheSize == null )
{
// TODO Really have System.out here?
System.out.println( "Cache not enabled for '"
+ URI_PROPERTY_KEY + "' Setting it ("
+ luceneIndex.getClass().getSimpleName()
+ "#enableCache" + ") to "
+ DEFAULT_URI_CACHE_SIZE );
try
{
luceneIndex.enableCache( URI_PROPERTY_KEY,
DEFAULT_URI_CACHE_SIZE );
} catch (UnsupportedOperationException uoe)
{
System.out.println("INFO: Could not enable cache: " + uoe);
}
}
}
}
public FulltextIndex getFulltextIndex()
{
return this.fulltextIndex;
}
protected GraphDatabaseService graphDB()
{
return this.graphDb;
}
public IndexService index()
{
return this.index;
}
protected GraphDatabaseUtil graphDbUtil()
{
return this.graphDbUtil;
}
// protected MetaStructure meta()
// {
// return this.meta;
// }
// protected void debug( String message )
// {
// System.out.println( message );
// }
//
// private void debugRelationship( Relationship relationship,
// boolean create )
// {
// String sign = create ? "+" : "-";
// debug( "\t" + sign + "Relationship (" + relationship + ") " +
// relationship.getStartNode() + " --[" +
// relationship.getType().name() + "]--> " +
// relationship.getEndNode() );
// }
// protected void debugCreateRelationship( Relationship relationship )
// {
// debugRelationship( relationship, true );
// }
// protected void debugDeleteRelationship( Relationship relationship )
// {
// debugRelationship( relationship, false );
// }
// protected void debugCreateNode( Node node, String uri )
// {
// debug( "\t+Node (" + node.getId() + ") " + ( uri == null ? "" : uri ) );
// }
// protected void debugDeleteNode( Node node, String uri )
// {
// debug( "\t-Node (" + node.getId() + ") " + ( uri == null ? "" : uri ) );
// }
protected String getNodeUri( AbstractNode node )
{
Uri uri = node.getUriOrNull();
return uri == null ? null : uri.getUriAsString();
}
public Node lookupNode( AbstractNode abstractNode )
{
Node result = null;
// if ( isMeta( abstractNode ) )
// {
// MetaStructureThing thing = getMetaStructureThing( abstractNode );
// result = thing == null ? null : thing.node();
// }
// else
// {
String nodeUri = getNodeUri( abstractNode );
if ( null != nodeUri )
{
result = index().getSingleNode( URI_PROPERTY_KEY, nodeUri );
}
else
{
throw new UnsupportedOperationException(
"Nodes without URIs (e.g. Blank Nodes) not supported at the moment: "
+ abstractNode.toString() );
}
// }
return result;
}
protected NodeContext lookupOrCreateNode( AbstractNode abstractNode,
Map<AbstractNode, Node> nodeMapping )
{
Node node = lookupNode( abstractNode );
boolean created = false;
if ( node == null )
{
node = createNode( abstractNode, nodeMapping );
created = true;
}
else
{
if ( nodeMapping != null )
{
nodeMapping.put( abstractNode, node );
}
}
return new NodeContext( node, created );
}
protected void deleteNode( Node node, Uri uriOrNull )
{
// debugDeleteNode( node, uriOrNull == null ? null :
// uriOrNull.getUriAsString() );
node.delete();
if ( uriOrNull != null )
{
index().removeIndex( node, URI_PROPERTY_KEY,
uriOrNull.getUriAsString() );
}
}
protected void deleteNodeIfEmpty( AbstractNode abstractNode, Node node )
{
if ( nodeIsEmpty( abstractNode, node, true ) )
{
deleteNode( node, abstractNode.getUriOrNull() );
}
}
// protected MetaStructureThing getMetaStructureThing( AbstractNode node )
// {
// MetaStructureThing thing = null;
// String metaInfo = getMetaExecutorInfo( node );
// if ( node.getUriOrNull() == null )
// {
// }
// else if ( metaInfo.equals( "class" ) )
// {
// thing = meta().getGlobalNamespace().getMetaClass(
// node.getUriOrNull().getUriAsString(), false );
// }
// else if ( metaInfo.equals( "property" ) )
// {
// thing = meta().getGlobalNamespace().getMetaProperty(
// node.getUriOrNull().getUriAsString(), false );
// }
// else
// {
// throw new IllegalArgumentException( "Strange meta info '" +
// metaInfo + "'" );
// }
// return thing;
// }
public String getNodeUriPropertyKey( AbstractNode abstractNode )
{
// return isMeta( abstractNode ) ? MetaStructureObject.KEY_NAME :
// URI_PROPERTY_KEY;
return URI_PROPERTY_KEY;
}
protected Relationship findDirectRelationship( Node startNode,
RelationshipType relType, Node endNode, Direction directionOrNull )
{
Relationship relationship = null;
Iterable<Relationship> relationships = directionOrNull == null ? startNode.getRelationships( relType )
: startNode.getRelationships( relType, directionOrNull );
for ( Relationship rel : relationships )
{
if ( rel.getOtherNode( startNode ).equals( endNode ) )
{
relationship = rel;
break;
}
}
return relationship;
}
protected void ensureDirectlyDisconnected( Node startNode,
AbstractRelationship abstractRelationship, Node endNode )
{
ensureDirectlyDisconnected( startNode, abstractRelationship, endNode,
Direction.OUTGOING );
}
protected void ensureDirectlyDisconnected( Node startNode,
AbstractRelationship abstractRelationship, Node endNode,
Direction optimizeDirection )
{
Node start = optimizeDirection == Direction.OUTGOING ? startNode
: endNode;
Node end = optimizeDirection == Direction.OUTGOING ? endNode
: startNode;
RelationshipType relType = relationshipType( abstractRelationship.getRelationshipTypeName() );
Relationship relationship = findDirectRelationship( start, relType,
end, optimizeDirection );
if ( relationship != null )
{
deleteRelationship( relationship );
}
}
protected Relationship ensureDirectlyConnected( Node startNode,
AbstractRelationship abstractRelationship, Node endNode )
{
return ensureDirectlyConnected( startNode, abstractRelationship,
endNode, Direction.OUTGOING );
}
protected Relationship ensureDirectlyConnected( Node startNode,
AbstractRelationship abstractRelationship, Node endNode,
Direction optimizeDirection )
{
Node start = optimizeDirection == Direction.OUTGOING ? startNode
: endNode;
Node end = optimizeDirection == Direction.OUTGOING ? endNode
: startNode;
RelationshipType relType = relationshipType( abstractRelationship.getRelationshipTypeName() );
Relationship relationship = findDirectRelationship( start, relType,
end, optimizeDirection );
if ( relationship == null )
{
createRelationship( start, abstractRelationship, end );
}
return relationship;
}
protected RelationshipType relationshipType( String name )
{
return DynamicRelationshipType.withName( name );
}
protected String getMetaExecutorInfo( AbstractNode node )
{
return (String) node.getSingleExecutorInfo( META_EXECUTOR_INFO_KEY );
}
// protected boolean isMeta( AbstractNode node )
// {
// return meta() != null && getMetaExecutorInfo( node ) != null;
// }
protected boolean containsProperties( PropertyContainer container,
Map<String, Collection<Object>> containingProperties,
Collection<String> excludeThese )
{
for ( Map.Entry<String, Collection<Object>> entry : containingProperties.entrySet() )
{
String key = entry.getKey();
if ( excludeThese.contains( key ) )
{
continue;
}
Collection<Object> values = entry.getValue();
Collection<Object> rawValues = graphDbUtil().getPropertyValues(
container, key );
if ( !rawValues.containsAll( values ) )
{
return false;
}
}
return true;
}
protected Map<AbstractNode, Node> getWellKnownNodeMappings(
AbstractRepresentation representation )
{
Map<AbstractNode, Node> nodeMapping = new HashMap<AbstractNode, Node>();
for ( AbstractNode abstractNode : representation.nodes() )
{
if ( abstractNode.getUriOrNull() != null )
{
Node node = lookupNode( abstractNode );
if ( node == null )
{
return null;
}
nodeMapping.put( abstractNode, node );
}
}
return nodeMapping;
}
protected boolean applyRepresentation( AbstractElement abstractElement,
PropertyContainer container )
{
boolean changed = false;
for ( Map.Entry<String, Collection<Object>> entry : abstractElement.properties().entrySet() )
{
Collection<Object> rawValues = new PropertyArraySet<Object>(
container, entry.getKey() );
for ( Object value : entry.getValue() )
{
boolean added = rawValues.add( value );
if ( added )
{
changed = true;
// debug( "\t+Property" + " (" + container + ") "
// + entry.getKey() + " " + "[" + value + "]" );
}
}
}
return changed;
}
protected boolean removeRepresentation( AbstractElement abstractElement,
PropertyContainer container )
{
boolean changed = false;
for ( Map.Entry<String, Collection<Object>> entry : abstractElement.properties().entrySet() )
{
Collection<Object> rawValues = new PropertyArraySet<Object>(
container, entry.getKey() );
for ( Object value : entry.getValue() )
{
boolean removed = rawValues.remove( value );
if ( removed )
{
changed = true;
// debug( "\t-Property" + " (" + container + ") "
// + entry.getKey() + " " + "[" + value + "]" );
}
}
}
return changed;
}
protected boolean nodeIsEmpty( AbstractNode abstractNode, Node node,
boolean checkRelationships )
{
if ( checkRelationships && node.hasRelationship() )
{
return false;
}
String legalKey = getNodeUriPropertyKey( abstractNode );
for ( String key : node.getPropertyKeys() )
{
if ( !key.equals( legalKey ) )
{
return false;
}
}
return true;
}
protected boolean removeRepresentation( AbstractElement element,
PropertyContainer container, String key )
{
boolean someRemoved = false;
Collection<Object> rawValues = new PropertyArraySet<Object>(
container, key );
for ( Object value : element.properties().get( key ) )
{
boolean removed = rawValues.remove( value );
if ( removed )
{
someRemoved = true;
// debug( "\t-Property (" + container + ") "
// + key + " " + "[" + value + "]" );
}
}
return someRemoved;
}
protected Object getRealLiteralValue( Literal literal )
{
// TODO Conversion?
return literal.getValue();
}
protected void indexLiteral( Node node, Uri predicate, Object literalValue )
{
index().index( node, LITERAL_VALUE_KEY, literalValue );
if ( getFulltextIndex() != null )
{
getFulltextIndex().index( node, predicate, literalValue );
}
}
protected void removeLiteralIndex( Node node, Uri predicate,
Object literalValue )
{
index().removeIndex( node, LITERAL_VALUE_KEY, literalValue );
if ( getFulltextIndex() != null )
{
getFulltextIndex().removeIndex( node, predicate, literalValue );
}
}
public Iterable<Node> findLiteralNodes( Object value )
{
return index().getNodes( LITERAL_VALUE_KEY, value );
}
protected Relationship createRelationship( Node from,
AbstractRelationship abstractRelationship, Node to )
{
Relationship relationship = from.createRelationshipTo(
to,
relationshipType( abstractRelationship.getRelationshipTypeName() ) );
applyRepresentation( abstractRelationship, relationship );
// debugCreateRelationship( relationship );
return relationship;
}
protected Node createNode( AbstractNode abstractNode,
Map<AbstractNode, Node> nodeMapping )
{
Node node = graphDb.createNode();
Uri uri = abstractNode.getUriOrNull();
if ( uri != null )
{
node.setProperty( URI_PROPERTY_KEY, uri.getUriAsString() );
index().index( node, URI_PROPERTY_KEY, uri.getUriAsString() );
}
applyRepresentation( abstractNode, node );
// debugCreateNode( node, uri == null ? null : uri.toString() );
if ( nodeMapping != null )
{
nodeMapping.put( abstractNode, node );
}
return node;
}
protected void deleteRelationship( Relationship relationship )
{
getLockManager().getReadLock( relationship );
try
{
relationship.delete();
}
catch ( NotFoundException e )
{
// ok some other thread deleted it
}
finally
{
getLockManager().releaseReadLock( relationship );
}
}
private LockManager getLockManager()
{
return ( (EmbeddedGraphDatabase) graphDb ).getConfig().getLockManager();
}
protected static class NodeContext
{
private Node node;
private boolean created;
protected NodeContext( Node node, boolean created )
{
this.node = node;
this.created = created;
}
protected Node getNode()
{
return this.node;
}
protected boolean wasCreated()
{
return this.created;
}
}
}