package org.neo4j.rdf.store.representation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.neo4j.rdf.model.Value;
/**
* An abstract representation of an arbitrary number of RDF statements in the
* node space. It is created by a {@link RepresentationStrategy}. An instance
* of this type is abstract in the sense that it doesn't encapsulate "real"
* Neo4j nodes but instead {@link AbstractNode abstract nodes} which are purely
* in-memory. It's important to remember that it's not just a description of the
* structure of some statements in general, but rather it represents the
* structure and properties of some <i>specific</i> statements, i.e. the nodes
* and relationships are actually populated with real values.
*/
public class AbstractRepresentation
{
private final Map<Object, AbstractNode> nodeKeys =
new HashMap<Object, AbstractNode>();
private final List<AbstractNode> nodes = new ArrayList<AbstractNode>();
private final List<AbstractRelationship> relationships =
new ArrayList<AbstractRelationship>();
/**
* Adds an {@link AbstractNode} to the representation.
* @param node the node to add to the representation.
*/
public void addNode( AbstractNode node )
{
if ( !this.nodes.contains( node ) )
{
this.nodes.add( node );
if ( node.getKey() != null )
{
this.nodeKeys.put( node.getKey(), node );
}
}
}
public AbstractNode node( Object key )
{
return nodeKeys.get( key );
}
/**
* Adds an {@link AbstractRelationship} to the representation.
* @param relationship the relationship to add to the representation.
*/
public void addRelationship( AbstractRelationship relationship )
{
if ( !this.relationships.contains( relationship ) )
{
this.relationships.add( relationship );
}
}
/**
* Returns the nodes that build up this statement in the node space
* @return the nodes that build up this statement in the node space
*/
public Iterable<AbstractNode> nodes()
{
return Collections.unmodifiableList( nodes );
}
/**
* Returns the relationships that connect the nodes of this statement in
* the node space.<br/>
* Invariant: the nodes at both ends of each relationship in this set
* are guaranteed to be part of the set of nodes returned by
* {@link #nodes()}.
* @return the relationships that connect this representation of a statement
*/
public Iterable<AbstractRelationship> relationships()
{
return Collections.unmodifiableList( relationships );
}
// Maybe add this?
public Map<AbstractElement, Value> getMappingToRdfValues()
{
return Collections.emptyMap();
}
/**
* http://en.wikipedia.org/wiki/DOT_language
* @return this representation as a DOT graph, good for debugging.
*/
public String toDotFormat()
{
CharRef counter = new CharRef();
counter.ch = 'a';
Map<AbstractNode, String> nameMap = new HashMap<AbstractNode, String>();
StringBuffer result = new StringBuffer( "digraph representation\n" );
result.append( "{\n" );
for ( AbstractRelationship relationship : relationships() )
{
String[] start = getNodeNameAndLabel( nameMap,
relationship.getStartNode(), counter );
if ( start[ 1 ] != null )
{
result.append( "\t" + start[ 0 ] + " [label=\"" +
start[ 1 ] + "\"];\n" );
}
String[] end = getNodeNameAndLabel( nameMap,
relationship.getEndNode(), counter );
if ( end[ 1 ] != null )
{
result.append( "\t" + end[ 0 ] + " [label=\"" +
end[ 1 ] + "\"];\n" );
}
result.append( "\t" + start[ 0 ] + " -> " + end[ 0 ] +
" [label=\"" + relationship.getRelationshipTypeName().substring(
relationship.getRelationshipTypeName().length() - 15 ) +
"\"];\n" );
}
result.append( "}" );
return result.toString();
}
private String[] getNodeNameAndLabel( Map<AbstractNode, String> nameMap,
AbstractNode node, CharRef counter )
{
String name;
String label = null;
if ( !nameMap.containsKey( node ) )
{
name = String.valueOf( counter.ch );
counter.ch++;
nameMap.put( node, name );
if ( node.getUriOrNull() != null )
{
label = node.getUriOrNull().getUriAsString().substring(
node.getUriOrNull().getUriAsString().length() - 15 );
}
else if ( node.getWildcardOrNull() != null )
{
label = "?" + node.getWildcardOrNull().getVariableName();
}
else if ( !node.properties().isEmpty() )
{
for ( String key : node.properties().keySet() )
{
for ( Object value : node.properties().get( key ) )
{
label = label == null ? "'" + value + "'" :
", '" + value + "'";
}
}
}
}
else
{
name = nameMap.get( node );
}
return new String[] { name, label };
}
private static class CharRef
{
private char ch;
}
}