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 MetaModelThing { /** * @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>( graphDb(), 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 properties directly related to * this class. */ public Collection<MetaModelProperty> getDirectProperties() { return new ObjectCollection<MetaModelProperty>( graphDb(), node(), MetaModelRelTypes.META_CLASS_HAS_PROPERTY, Direction.OUTGOING, model(), MetaModelProperty.class ); } /** * @return an unmodifiable collection of all properties related to this * class. */ public Collection<MetaModelProperty> getAllProperties() { Transaction tx = graphDb().beginTx(); try { HashSet<MetaModelProperty> properties = new HashSet<MetaModelProperty>(); for ( Node node : node().traverse( Traverser.Order.BREADTH_FIRST, StopEvaluator.END_OF_GRAPH, // Maybe remove these three lines? They go for subproperties too new AllPropertiesRE(), MetaModelRelTypes.META_IS_SUBPROPERTY_OF, Direction.INCOMING, MetaModelRelTypes.META_CLASS_HAS_PROPERTY, Direction.OUTGOING, MetaModelRelTypes.META_IS_SUBCLASS_OF, Direction.OUTGOING ) ) { properties.add( new MetaModelProperty( model(), node ) ); } return Collections.unmodifiableSet( properties ); } finally { tx.finish(); } } /** * @param property the {@link MetaModelProperty} to associate with. * @param allowCreate whether to allow creation of the restriction if * it doesn't exist. * @return the restriction for {@code property} or creates a new if * {@code allowCreate} is {@code true}. */ public MetaModelRestriction getRestriction( MetaModelProperty property, boolean allowCreate ) { Transaction tx = graphDb().beginTx(); try { Collection<MetaModelRestriction> restrictions = getDirectRestrictions(); for ( MetaModelRestriction restriction : restrictions ) { if ( restriction.getMetaProperty().equals( property ) ) { return restriction; } } if ( !allowCreate ) { return null; } // if ( !getAllProperties().contains( property ) ) // { // throw new RuntimeException( this + " isn't in the domain of " + // property + " add it first" ); // } Node node = graphDb().createNode(); MetaModelRestriction result = new MetaModelRestriction( model(), node ); restrictions.add( result ); node.createRelationshipTo( property.node(), MetaModelRelTypes.META_RESTRICTION_TO_PROPERTY ); tx.success(); return result; } finally { tx.finish(); } } /** * @return the restrictions for this class. */ public Collection<MetaModelRestriction> getDirectRestrictions() { return new ObjectCollection<MetaModelRestriction>( graphDb(), node(), MetaModelRelTypes.META_RESTRICTION_TO_CLASS, Direction.INCOMING, model(), MetaModelRestriction.class ); } /** * @return an unmodifiable collection of all direct restrictions as well * as restrictions for super classes. */ public Collection<MetaModelRestriction> getAllRestrictions() { Transaction tx = graphDb().beginTx(); try { HashSet<MetaModelRestriction> restrictions = new HashSet<MetaModelRestriction>(); for ( Node node : node().traverse( Traverser.Order.BREADTH_FIRST, StopEvaluator.END_OF_GRAPH, new OneOfRelTypesReturnableEvaluator( MetaModelRelTypes.META_RESTRICTION_TO_CLASS ), MetaModelRelTypes.META_RESTRICTION_TO_CLASS, Direction.INCOMING, MetaModelRelTypes.META_IS_SUBCLASS_OF, Direction.OUTGOING ) ) { restrictions.add( new MetaModelRestriction( model(), node ) ); } return Collections.unmodifiableSet( restrictions ); } finally { tx.finish(); } } /** * @return a modifiable collection of instances of this class. */ public Collection<Node> getInstances() { return new InstanceCollection( graphDb(), node(), model() ); } private class AllPropertiesRE 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_SUBPROPERTY_OF ) ) { if ( currentPos.currentNode().hasRelationship( MetaModelRelTypes.META_CLASS_HAS_PROPERTY ) ) { return false; } } return true; } } }