package org.neo4j.meta.model;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.ReturnableEvaluator;
import org.neo4j.graphdb.StopEvaluator;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.TraversalPosition;
import org.neo4j.graphdb.Traverser;
import org.neo4j.util.OneOfRelTypesReturnableEvaluator;
/**
* Represents a class in the meta model.
*/
public class MetaModelClass extends MetaModelPropertyContainer implements MetaModelRestrictable<InstanceRange>
{
/**
* @param model the {@link MetaModel} instance.
* @param node the {@link Node} to wrap.
*/
public MetaModelClass( MetaModel model, Node node )
{
super( model, node );
}
private Collection<MetaModelClass> hierarchyCollection(
Direction direction )
{
return new ObjectCollection<MetaModelClass>(
node(), MetaModelRelTypes.META_IS_SUBCLASS_OF, direction,
model(), MetaModelClass.class );
}
@Override
public Collection<MetaModelClass> getDirectSubs()
{
return hierarchyCollection( Direction.INCOMING );
}
@Override
public Collection<MetaModelClass> getDirectSupers()
{
return hierarchyCollection( Direction.OUTGOING );
}
@Override
protected RelationshipType subRelationshipType()
{
return MetaModelRelTypes.META_IS_SUBCLASS_OF;
}
/**
* @return a modifiable collection of RelationshipTypes directly related to
* this class.
*/
public Collection<MetaModelRelationship> getDirectRelationships()
{
return new ObjectCollection<MetaModelRelationship>(
node(), MetaModelRelTypes.META_CLASS_HAS_RELATIONSHIP,
Direction.OUTGOING, model(), MetaModelRelationship.class );
}
/**
* @return an unmodifiable collection of all relationships related to this
* class.
*/
public Collection<MetaModelRelationship> getAllRelationships()
{
Transaction tx = graphDb().beginTx();
try
{
HashSet<MetaModelRelationship> relationshipTypes =
new HashSet<MetaModelRelationship>();
for ( Node node : node().traverse( Traverser.Order.BREADTH_FIRST,
StopEvaluator.END_OF_GRAPH,
// Maybe remove these three lines? They go for subrelationships too
new AllRelationshipTypesRE(),
MetaModelRelTypes.META_IS_SUBRELATIONSHIP_OF,
Direction.INCOMING,
MetaModelRelTypes.META_CLASS_HAS_RELATIONSHIP,
Direction.OUTGOING,
MetaModelRelTypes.META_IS_SUBCLASS_OF,
Direction.OUTGOING ) )
{
relationshipTypes.add( new MetaModelRelationship( model(), node ) );
}
return Collections.unmodifiableSet( relationshipTypes );
}
finally
{
tx.finish();
}
}
/**
* @param relationshipType the {@link MetaModelRelationship} to associate with.
* @param allowCreate whether to allow creation of the restriction if
* it doesn't exist.
* @return the restriction for {@code Relationship} or creates a new if
* {@code allowCreate} is {@code true}.
*/
public MetaModelRelationshipRestriction getRestriction(
MetaModelRelationship relationshipType, boolean allowCreate )
{
Transaction tx = graphDb().beginTx();
try
{
Collection<MetaModelRelationshipRestriction> restrictions =
getDirectRelationshipRestrictions();
for ( MetaModelRelationshipRestriction restriction : restrictions )
{
if ( restriction.getMetaRelationshipType().equals( relationshipType ) )
{
return restriction;
}
}
if ( !allowCreate )
{
return null;
}
Node node = graphDb().createNode();
MetaModelRelationshipRestriction result = new MetaModelRelationshipRestriction(
model(), node );
restrictions.add( result );
node.createRelationshipTo( relationshipType.node(),
MetaModelRelTypes.META_RESTRICTION_TO_RELATIONSHIP );
tx.success();
return result;
}
finally
{
tx.finish();
}
}
/**
* @return the restrictions for this class.
*/
public Collection<MetaModelRelationshipRestriction> getDirectRelationshipRestrictions()
{
return new ObjectCollection<MetaModelRelationshipRestriction>(
node(), MetaModelRelTypes.META_RELATIONSHIP_RESTRICTION_TO_CLASS,
Direction.INCOMING, model(), MetaModelRelationshipRestriction.class );
}
/**
* @return an unmodifiable collection of all direct restrictions as well
* as restrictions for super classes.
*/
/**
* @return an unmodifiable collection of all direct restrictions as well
* as restrictions for super classes.
*/
public Collection<MetaModelRelationshipRestriction> getAllRelationshipRestrictions()
{
Transaction tx = graphDb().beginTx();
try
{
HashSet<MetaModelRelationshipRestriction> restrictions =
new HashSet<MetaModelRelationshipRestriction>();
for ( Node node : node().traverse( Traverser.Order.BREADTH_FIRST,
StopEvaluator.END_OF_GRAPH,
new OneOfRelTypesReturnableEvaluator(
MetaModelRelTypes.META_RELATIONSHIP_RESTRICTION_TO_CLASS ),
MetaModelRelTypes.META_RELATIONSHIP_RESTRICTION_TO_CLASS,
Direction.INCOMING,
MetaModelRelTypes.META_IS_SUBCLASS_OF,
Direction.OUTGOING ) )
{
restrictions.add(
new MetaModelRelationshipRestriction( model(), node ) );
}
return Collections.unmodifiableSet( restrictions );
}
finally
{
tx.finish();
}
}
/**
* @return a modifiable collection of instances of this class.
*/
public Collection<Node> getDirectInstances()
{
return new InstanceCollection( node(), model() );
}
/**
* @return all instances of this class, including instances of subclasses
*/
public Iterable<Node> getAllInstances()
{
return new RecursiveInstanceTraverser( graphDb(), node(), model() );
}
public void setMinCardinality( Integer cardinalityOrNull )
{
setOrRemoveProperty( KEY_MIN_CARDINALITY, cardinalityOrNull );
}
public Integer getMinCardinality()
{
return ( Integer ) node().getProperty( KEY_MIN_CARDINALITY, null );
}
public void setMaxCardinality( Integer cardinalityOrNull )
{
setOrRemoveProperty( KEY_MAX_CARDINALITY, cardinalityOrNull );
}
public Integer getMaxCardinality()
{
return ( Integer ) node().getProperty( KEY_MAX_CARDINALITY, null );
}
public void setCardinality( Integer cardinality )
{
Transaction tx = graphDb().beginTx();
try
{
setMinCardinality( cardinality );
setMaxCardinality( cardinality );
tx.success();
}
finally
{
tx.finish();
}
}
public void setRange( InstanceRange range )
{
InstanceRange.setOrRemoveRange( this, range );
}
public InstanceRange getRange()
{
return InstanceRange.loadRange( this );
}
private class AllRelationshipTypesRE implements ReturnableEvaluator
{
private boolean same( RelationshipType r1,
RelationshipType r2 )
{
return r1.name().equals( r2.name() );
}
public boolean isReturnableNode( TraversalPosition currentPos )
{
Relationship lastRel =
currentPos.lastRelationshipTraversed();
if ( lastRel == null || same( lastRel.getType(),
MetaModelRelTypes.META_IS_SUBCLASS_OF ) )
{
return false;
}
if ( same( lastRel.getType(),
MetaModelRelTypes.META_IS_SUBRELATIONSHIP_OF ) )
{
if ( currentPos.currentNode().hasRelationship(
MetaModelRelTypes.META_CLASS_HAS_RELATIONSHIP ) )
{
return false;
}
}
return true;
}
}
}