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; /** * The super class of {@link MetaModelRelationship} and * {@link MetaModelClass}. It contains hierarchical functionality. */ public abstract class MetaModelPropertyContainer extends MetaModelThing{ MetaModelPropertyContainer( MetaModel model, Node node ) { super( model, node ); } 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_SUBRELATIONSHIP_OF ) || 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_HAS_PROPERTY ) ) { return false; } } return true; } } /** * @return a modifiable collection of properties directly related to * this class. */ public Collection<MetaModelProperty> getDirectProperties() { return new ObjectCollection<MetaModelProperty>( node(), MetaModelRelTypes.META_HAS_PROPERTY, Direction.OUTGOING, model(), MetaModelProperty.class ); } /** * @return an unmodifiable collection of all properties related to this * relationshiptype. */ 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 four lines? They go for subproperties too new AllPropertiesRE(), MetaModelRelTypes.META_IS_SUBPROPERTY_OF, Direction.INCOMING, MetaModelRelTypes.META_HAS_PROPERTY, Direction.OUTGOING, MetaModelRelTypes.META_IS_SUBRELATIONSHIP_OF, Direction.OUTGOING, MetaModelRelTypes.META_IS_SUBCLASS_OF, Direction.OUTGOING ) ) { properties.add( new MetaModelProperty( model(), node ) ); } return Collections.unmodifiableSet( properties ); } finally { tx.finish(); } } /** * @return the restrictions for this relationshiptype. */ public Collection<MetaModelPropertyRestriction> getDirectPropertyRestrictions() { return new ObjectCollection<MetaModelPropertyRestriction>( node(), MetaModelRelTypes.META_PROPERTY_RESTRICTION_TO_PROPERTYCONTAINER, Direction.INCOMING, model(), MetaModelPropertyRestriction.class ); } /** * @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 MetaModelPropertyRestriction getRestriction( MetaModelProperty property, boolean allowCreate ) { Transaction tx = graphDb().beginTx(); try { Collection<MetaModelPropertyRestriction> restrictions = getDirectPropertyRestrictions(); for ( MetaModelPropertyRestriction 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(); MetaModelPropertyRestriction result = new MetaModelPropertyRestriction( model(), node ); restrictions.add( result ); node.createRelationshipTo( property.node(), MetaModelRelTypes.META_RESTRICTION_TO_PROPERTY ); tx.success(); return result; } finally { tx.finish(); } } public Collection<MetaModelPropertyRestriction> getAllPropertyRestrictions() { Transaction tx = graphDb().beginTx(); try { HashSet<MetaModelPropertyRestriction> restrictions = new HashSet<MetaModelPropertyRestriction>(); for ( Node node : node().traverse( Traverser.Order.BREADTH_FIRST, StopEvaluator.END_OF_GRAPH, new OneOfRelTypesReturnableEvaluator( MetaModelRelTypes.META_PROPERTY_RESTRICTION_TO_PROPERTYCONTAINER ), MetaModelRelTypes.META_PROPERTY_RESTRICTION_TO_PROPERTYCONTAINER, Direction.INCOMING, MetaModelRelTypes.META_IS_SUBCLASS_OF, Direction.OUTGOING, MetaModelRelTypes.META_IS_SUBRELATIONSHIP_OF, Direction.OUTGOING )) { restrictions.add( new MetaModelPropertyRestriction( model(), node ) ); } return Collections.unmodifiableSet( restrictions ); } finally { tx.finish(); } } public void setCollectionBehaviourClass( Class<? extends Collection> collectionClassOrNull ) { setOrRemoveProperty( KEY_COLLECTION_CLASS, collectionClassOrNull == null ? null : collectionClassOrNull.getName() ); } public Class<? extends Collection<?>> getCollectionBehaviourClass() { try { String className = ( String ) node().getProperty( KEY_COLLECTION_CLASS, null ); // Yep generics warning, but what're you going to do? return className == null ? null : ( Class<? extends Collection<?>> ) Class.forName( className ); } catch ( Exception e ) { throw new RuntimeException( e ); } } }