From a394361e114f981e4babdb7abca80ea31dfefbce Mon Sep 17 00:00:00 2001 From: David Montag Date: Tue, 24 Aug 2010 14:51:44 +0200 Subject: [PATCH] Extracted inner classes from aspect to top level classes. --- .../graph/neo4j/FieldAccessor.java | 11 + .../graph/neo4j/FieldAccessorFactory.java | 76 +++++ .../graph/neo4j/ManagedFieldAccessorSet.java | 46 +++ .../graph/neo4j/Neo4jNodeBacking.aj | 319 +----------------- ...OneToNRelationshipEntityFieldAccessor.java | 39 +++ .../OneToNRelationshipFieldAccessor.java | 81 +++++ ...adOnlyOneToNRelationshipFieldAccessor.java | 44 +++ .../SingleRelationshipFieldAccessor.java | 62 ++++ 8 files changed, 368 insertions(+), 310 deletions(-) create mode 100644 src/main/java/org/springframework/persistence/graph/neo4j/FieldAccessor.java create mode 100644 src/main/java/org/springframework/persistence/graph/neo4j/FieldAccessorFactory.java create mode 100644 src/main/java/org/springframework/persistence/graph/neo4j/ManagedFieldAccessorSet.java create mode 100644 src/main/java/org/springframework/persistence/graph/neo4j/OneToNRelationshipEntityFieldAccessor.java create mode 100644 src/main/java/org/springframework/persistence/graph/neo4j/OneToNRelationshipFieldAccessor.java create mode 100644 src/main/java/org/springframework/persistence/graph/neo4j/ReadOnlyOneToNRelationshipFieldAccessor.java create mode 100644 src/main/java/org/springframework/persistence/graph/neo4j/SingleRelationshipFieldAccessor.java diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/FieldAccessor.java b/src/main/java/org/springframework/persistence/graph/neo4j/FieldAccessor.java new file mode 100644 index 000000000..8f39dc0d7 --- /dev/null +++ b/src/main/java/org/springframework/persistence/graph/neo4j/FieldAccessor.java @@ -0,0 +1,11 @@ +package org.springframework.persistence.graph.neo4j; + +public interface FieldAccessor { + + // Set entity field to newVal + Object apply(NodeBacked entity, Object newVal); + + // Read object from entity field + Object readObject(NodeBacked entity); + +} diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/FieldAccessorFactory.java b/src/main/java/org/springframework/persistence/graph/neo4j/FieldAccessorFactory.java new file mode 100644 index 000000000..24660094d --- /dev/null +++ b/src/main/java/org/springframework/persistence/graph/neo4j/FieldAccessorFactory.java @@ -0,0 +1,76 @@ +package org.springframework.persistence.graph.neo4j; + +import java.lang.reflect.Field; +import java.util.Collection; + +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.DynamicRelationshipType; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.springframework.persistence.graph.Graph; +import org.springframework.persistence.support.EntityInstantiator; + +public class FieldAccessorFactory { + private final EntityInstantiator graphEntityInstantiator; + private final EntityInstantiator relationshipEntityInstantiator; + + public FieldAccessorFactory(EntityInstantiator graphEntityInstantiator, EntityInstantiator relationshipEntityInstantiator) { + this.graphEntityInstantiator = graphEntityInstantiator; + this.relationshipEntityInstantiator = relationshipEntityInstantiator; + } + + public FieldAccessor forField(Field field) { + Graph.Entity.Relationship relAnnotation = field.getAnnotation(Graph.Entity.Relationship.class); + if (isSingleRelationshipField(field)) { + Class relatedType = (Class) field.getType(); + if (relAnnotation != null) { + return new SingleRelationshipFieldAccessor(DynamicRelationshipType.withName(relAnnotation.type()), + relAnnotation.direction().toNeo4jDir(), relatedType, graphEntityInstantiator); + } + return new SingleRelationshipFieldAccessor(DynamicRelationshipType.withName(getNeo4jPropertyName(field)), + Direction.OUTGOING, relatedType, graphEntityInstantiator); + } + if (isOneToNRelationshipField(field)) { + return new OneToNRelationshipFieldAccessor(DynamicRelationshipType.withName(relAnnotation.type()), + relAnnotation.direction().toNeo4jDir(), relAnnotation.elementClass(), graphEntityInstantiator); + } + if (isReadOnlyOneToNRelationshipField(field)) { + return new ReadOnlyOneToNRelationshipFieldAccessor(DynamicRelationshipType.withName(relAnnotation.type()), + relAnnotation.direction().toNeo4jDir(), relAnnotation.elementClass(), graphEntityInstantiator); + } + if (isOneToNRelationshipEntityField(field)) { + Graph.Entity.RelationshipEntity relEntityAnnotation = field.getAnnotation(Graph.Entity.RelationshipEntity.class); + return new OneToNRelationshipEntityFieldAccessor(DynamicRelationshipType.withName(relEntityAnnotation.type()), + relEntityAnnotation.direction().toNeo4jDir(), relEntityAnnotation.elementClass(), relationshipEntityInstantiator); + } + throw new IllegalArgumentException("Not a Neo4j relationship field: " + field); + } + + private static boolean isSingleRelationshipField(Field f) { + return NodeBacked.class.isAssignableFrom(f.getType()); + } + + private static boolean isOneToNRelationshipField(Field f) { + if (!Collection.class.isAssignableFrom(f.getType())) return false; + Graph.Entity.Relationship relAnnotation = f.getAnnotation(Graph.Entity.Relationship.class); + return relAnnotation != null && NodeBacked.class.isAssignableFrom(relAnnotation.elementClass()) && !relAnnotation.elementClass().equals(NodeBacked.class); + } + + private static boolean isReadOnlyOneToNRelationshipField(Field f) { + Graph.Entity.Relationship relAnnotation = f.getAnnotation(Graph.Entity.Relationship.class); + return Iterable.class.equals(f.getType()) + && relAnnotation != null + && !NodeBacked.class.equals(relAnnotation.elementClass()); + } + + private static boolean isOneToNRelationshipEntityField(Field f) { + Graph.Entity.RelationshipEntity relEntityAnnotation = f.getAnnotation(Graph.Entity.RelationshipEntity.class); + return Iterable.class.isAssignableFrom(f.getType()) + && relEntityAnnotation != null + && !RelationshipBacked.class.equals(relEntityAnnotation.elementClass()); + } + + public static String getNeo4jPropertyName(Field field) { + return String.format("%s.%s",field.getDeclaringClass().getSimpleName(),field.getName()); + } +} \ No newline at end of file diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/ManagedFieldAccessorSet.java b/src/main/java/org/springframework/persistence/graph/neo4j/ManagedFieldAccessorSet.java new file mode 100644 index 000000000..56e880453 --- /dev/null +++ b/src/main/java/org/springframework/persistence/graph/neo4j/ManagedFieldAccessorSet.java @@ -0,0 +1,46 @@ +package org.springframework.persistence.graph.neo4j; + +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Set; + +public class ManagedFieldAccessorSet extends AbstractSet { + private final NodeBacked entity; + final Set delegate; + private final FieldAccessor relationshipInfo; + + public ManagedFieldAccessorSet(NodeBacked entity, Object newVal, FieldAccessor relationshipInfo) { + this.entity = entity; + this.relationshipInfo = relationshipInfo; + delegate = (Set) newVal; + } + + @Override + public Iterator iterator() { + return delegate.iterator(); + } + + @Override + public int size() { + return delegate.size(); + } + + @Override + public boolean add(T e) { + boolean res = delegate.add(e); + if (res) { + relationshipInfo.apply(entity, delegate); + } + return res; + } + + @Override + public boolean remove(Object o) { + boolean res = delegate.remove(o); + if (res) { + relationshipInfo.apply(entity, delegate); + } + return res; + } + +} \ No newline at end of file diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj b/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj index 69e92affb..c777b5a5c 100644 --- a/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj +++ b/src/main/java/org/springframework/persistence/graph/neo4j/Neo4jNodeBacking.aj @@ -41,10 +41,8 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields graphEntityInstantiator; - - private GraphDatabaseUtil graphDatabaseUtil; - private RelationshipInfoFactory relationshipInfoFactory; + private FieldAccessorFactory fieldAccessorFactory; private EntityInstantiator relationshipEntityInstantiator; @@ -53,8 +51,7 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields fieldType = f.getType(); if (isPropertyType(fieldType)) { - String propName = getNeo4jPropertyName(f); + String propName = FieldAccessorFactory.getNeo4jPropertyName(f); entity.getUnderlyingNode().setProperty(propName, newVal); log.info("SET " + f + " -> Neo4J simple node property [" + propName + "] with value=[" + newVal + "]"); return proceed(entity, newVal); } - RelationshipInfo relInfo = relationshipInfoFactory.forField(f); - if (relInfo == null) { + FieldAccessor accessor = fieldAccessorFactory.forField(f); + if (accessor == null) { log.info("Ignored SET " + f + ": " + f.getType().getName() + " not primitive or GraphEntity"); return proceed(entity, newVal); } log.info("SET " + f + " -> Neo4J relationship with value=[" + newVal + "]"); - Object result = relInfo.apply(entity, newVal); + Object result = accessor.apply(entity, newVal); return proceed(entity,result); } catch(NotInTransactionException e) { throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e); @@ -181,301 +177,4 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields graphEntityInstantiator; - private final EntityInstantiator relationshipEntityInstantiator; - - public RelationshipInfoFactory(EntityInstantiator graphEntityInstantiator, EntityInstantiator relationshipEntityInstantiator) { - this.graphEntityInstantiator = graphEntityInstantiator; - this.relationshipEntityInstantiator = relationshipEntityInstantiator; - } - - public RelationshipInfo forField(Field field) { - Graph.Entity.Relationship relAnnotation = field.getAnnotation(Graph.Entity.Relationship.class); - if (isSingleRelationshipField(field)) { - Class relatedType = (Class) field.getType(); - if (relAnnotation != null) { - return new SingleRelationshipInfo(DynamicRelationshipType.withName(relAnnotation.type()), - relAnnotation.direction().toNeo4jDir(), relatedType, graphEntityInstantiator); - } - return new SingleRelationshipInfo(DynamicRelationshipType.withName(getNeo4jPropertyName(field)), - Direction.OUTGOING, relatedType, graphEntityInstantiator); - } - if (isOneToNRelationshipField(field)) { - return new OneToNRelationshipInfo(DynamicRelationshipType.withName(relAnnotation.type()), - relAnnotation.direction().toNeo4jDir(), relAnnotation.elementClass(), graphEntityInstantiator); - } - if (isReadOnlyOneToNRelationshipField(field)) { - return new ReadOnlyOneToNRelationshipInfo(DynamicRelationshipType.withName(relAnnotation.type()), - relAnnotation.direction().toNeo4jDir(), relAnnotation.elementClass(), graphEntityInstantiator); - } - if (isOneToNRelationshipEntityField(field)) { - Graph.Entity.RelationshipEntity relEntityAnnotation = field.getAnnotation(Graph.Entity.RelationshipEntity.class); - return new OneToNRelationshipEntityInfo(DynamicRelationshipType.withName(relEntityAnnotation.type()), - relEntityAnnotation.direction().toNeo4jDir(), relEntityAnnotation.elementClass(), relationshipEntityInstantiator); - } - throw new IllegalArgumentException("Not a Neo4j relationship field: " + field); - } - - private static boolean isSingleRelationshipField(Field f) { - return NodeBacked.class.isAssignableFrom(f.getType()); - } - - private static boolean isOneToNRelationshipField(Field f) { - if (!Collection.class.isAssignableFrom(f.getType())) return false; - Graph.Entity.Relationship relAnnotation = f.getAnnotation(Graph.Entity.Relationship.class); - return relAnnotation != null && NodeBacked.class.isAssignableFrom(relAnnotation.elementClass()) && !relAnnotation.elementClass().equals(NodeBacked.class); - } - - private boolean isReadOnlyOneToNRelationshipField(Field f) { - Graph.Entity.Relationship relAnnotation = f.getAnnotation(Graph.Entity.Relationship.class); - return Iterable.class.equals(f.getType()) - && relAnnotation != null - && !NodeBacked.class.equals(relAnnotation.elementClass()); - } - - private boolean isOneToNRelationshipEntityField(Field f) { - Graph.Entity.RelationshipEntity relEntityAnnotation = f.getAnnotation(Graph.Entity.RelationshipEntity.class); - return Iterable.class.isAssignableFrom(f.getType()) - && relEntityAnnotation != null - && !RelationshipBacked.class.equals(relEntityAnnotation.elementClass()); - } - } - - public static class SingleRelationshipInfo implements RelationshipInfo { - - private final RelationshipType type; - private final Direction direction; - private final Class relatedType; - private final EntityInstantiator graphEntityInstantiator; - - public SingleRelationshipInfo(RelationshipType type, Direction direction, Class clazz, EntityInstantiator graphEntityInstantiator) { - this.type = type; - this.direction = direction; - this.relatedType = clazz; - this.graphEntityInstantiator = graphEntityInstantiator; - } - - public Object apply(NodeBacked entity, Object newVal) { - if (newVal != null && !(newVal instanceof NodeBacked)) { - throw new IllegalArgumentException("New value must be NodeBacked."); - } - Node entityNode = entity.getUnderlyingNode(); - for ( Relationship relationship : entityNode.getRelationships(type, direction) ) { - relationship.delete(); - } - if (newVal == null) { - return null; - } - Node targetNode = ((NodeBacked) newVal).getUnderlyingNode(); - if (entityNode.equals(targetNode)) { - throw new InvalidDataAccessApiUsageException("Cannot create circular reference."); - } - switch(direction) { - case OUTGOING : entityNode.createRelationshipTo(targetNode, type); break; - case INCOMING : targetNode.createRelationshipTo(entityNode, type); break; - default : throw new IllegalArgumentException("invalid direction " + direction); - } - return newVal; - } - - @Override - public Object readObject(NodeBacked entity) { - Node entityNode = entity.getUnderlyingNode(); - if (entityNode == null) { - throw new IllegalStateException("Entity must have a backing Node"); - } - Relationship singleRelationship = entityNode.getSingleRelationship(type, direction); - - if (singleRelationship == null) { - return null; - } - Node targetNode = singleRelationship.getOtherNode(entityNode); - return graphEntityInstantiator.createEntityFromState(targetNode, relatedType); - } - - } - - public static class OneToNRelationshipInfo implements RelationshipInfo { - - private final RelationshipType type; - private final Direction direction; - private final Class relatedType; - private final EntityInstantiator graphEntityInstantiator; - - public OneToNRelationshipInfo(RelationshipType type, Direction direction, Class elementClass, EntityInstantiator graphEntityInstantiator) { - this.type = type; - this.direction = direction; - this.relatedType = elementClass; - this.graphEntityInstantiator = graphEntityInstantiator; - } - - public Object apply(final NodeBacked entity, final Object newVal) { - Node entityNode = entity.getUnderlyingNode(); - - Set newNodes=new HashSet(); - if (newVal != null) { - if (!(newVal instanceof Set)) { - throw new IllegalArgumentException("New value must be a Set, was: " + newVal.getClass()); - } - Set set = (Set) newVal; - for (Object obj : set) { - if (!(obj instanceof NodeBacked)) { - throw new IllegalArgumentException("New value elements must be NodeBacked."); - } - Node newNode=((NodeBacked) obj).getUnderlyingNode(); - if (entityNode.equals(newNode)) { - throw new InvalidDataAccessApiUsageException("Cannot create circular reference."); - } - newNodes.add(newNode); - } - } - for ( Relationship relationship : entityNode.getRelationships(type, direction) ) { - if (!newNodes.remove(relationship.getOtherNode(entityNode))) - relationship.delete(); - } - if (newVal == null) { - return null; - } - - for (Node newNode : newNodes) { - switch(direction) { - case OUTGOING : entityNode.createRelationshipTo(newNode, type); break; - case INCOMING : newNode.createRelationshipTo(entityNode, type); break; - default : throw new IllegalArgumentException("invalid direction " + direction); - } - } - return new ManagedSet(entity, newVal,this); // TODO managedSet that for each mutating method calls this apply (todo use AspectJ to handle that?) - } - - @Override - public Object readObject(NodeBacked entity) { - Node entityNode = entity.getUnderlyingNode(); - if (entityNode == null) { - throw new IllegalStateException("Entity must have a backing Node"); - } - Set result = new HashSet(); - for (Relationship rel : entityNode.getRelationships(type, direction)) { - result.add(graphEntityInstantiator.createEntityFromState(rel.getOtherNode(entityNode), relatedType)); - } - return new ManagedSet(entity, result, this); - } - - } - - public static class ReadOnlyOneToNRelationshipInfo implements RelationshipInfo { - - private final RelationshipType type; - private final Direction direction; - private final Class relatedType; - private final EntityInstantiator graphEntityInstantiator; - - public ReadOnlyOneToNRelationshipInfo(RelationshipType type, Direction direction, Class elementClass, EntityInstantiator graphEntityInstantiator) { - this.type = type; - this.direction = direction; - this.relatedType = elementClass; - this.graphEntityInstantiator = graphEntityInstantiator; - } - - public Object apply(final NodeBacked entity, final Object newVal) { - throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field."); - } - - @Override - public Object readObject(NodeBacked entity) { - Node entityNode = entity.getUnderlyingNode(); - if (entityNode == null) { - throw new IllegalStateException("Entity must have a backing Node"); - } - Set result = new HashSet(); - for (Relationship rel : entityNode.getRelationships(type, direction)) { - result.add(graphEntityInstantiator.createEntityFromState(rel.getOtherNode(entityNode), relatedType)); - } - return new ManagedSet(entity, result, this); - } - - } - - public static class OneToNRelationshipEntityInfo implements RelationshipInfo { - - private final RelationshipType type; - private final Direction direction; - private final Class elementClass; - private final EntityInstantiator relationshipEntityInstantiator; - - public OneToNRelationshipEntityInfo(RelationshipType type, Direction direction, Class elementClass, EntityInstantiator relationshipEntityInstantiator) { - this.type = type; - this.direction = direction; - this.elementClass = elementClass; - this.relationshipEntityInstantiator = relationshipEntityInstantiator; - } - - @Override - public Object apply(NodeBacked entity, Object newVal) { - throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field."); - } - - @Override - public Object readObject(NodeBacked entity) { - Set result = new HashSet(); - for (Relationship rel : entity.getUnderlyingNode().getRelationships(type, direction)) { - result.add(relationshipEntityInstantiator.createEntityFromState(rel, elementClass)); - } - return new ManagedSet(entity, result, this); - } - } - - private static final class ManagedSet extends AbstractSet { - private final NodeBacked entity; - final Set delegate; - private final RelationshipInfo relationshipInfo; - - private ManagedSet(NodeBacked entity, Object newVal, RelationshipInfo relationshipInfo) { - this.entity = entity; - this.relationshipInfo = relationshipInfo; - delegate = (Set) newVal; - } - - @Override - public Iterator iterator() { - return delegate.iterator(); - } - - @Override - public int size() { - return delegate.size(); - } - - @Override - public boolean add(T e) { - boolean res = delegate.add(e); - if (res) { - relationshipInfo.apply(entity, delegate); - } - return res; - } - - @Override - public boolean remove(Object o) { - boolean res = delegate.remove(o); - if (res) { - relationshipInfo.apply(entity, delegate); - } - return res; - } - - } - } diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/OneToNRelationshipEntityFieldAccessor.java b/src/main/java/org/springframework/persistence/graph/neo4j/OneToNRelationshipEntityFieldAccessor.java new file mode 100644 index 000000000..bf1bc7715 --- /dev/null +++ b/src/main/java/org/springframework/persistence/graph/neo4j/OneToNRelationshipEntityFieldAccessor.java @@ -0,0 +1,39 @@ +package org.springframework.persistence.graph.neo4j; + +import java.util.HashSet; +import java.util.Set; + +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.persistence.support.EntityInstantiator; + +public class OneToNRelationshipEntityFieldAccessor implements FieldAccessor { + + private final RelationshipType type; + private final Direction direction; + private final Class elementClass; + private final EntityInstantiator relationshipEntityInstantiator; + + public OneToNRelationshipEntityFieldAccessor(RelationshipType type, Direction direction, Class elementClass, EntityInstantiator relationshipEntityInstantiator) { + this.type = type; + this.direction = direction; + this.elementClass = elementClass; + this.relationshipEntityInstantiator = relationshipEntityInstantiator; + } + + @Override + public Object apply(NodeBacked entity, Object newVal) { + throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field."); + } + + @Override + public Object readObject(NodeBacked entity) { + Set result = new HashSet(); + for (Relationship rel : entity.getUnderlyingNode().getRelationships(type, direction)) { + result.add(relationshipEntityInstantiator.createEntityFromState(rel, elementClass)); + } + return new ManagedFieldAccessorSet(entity, result, this); + } +} diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/OneToNRelationshipFieldAccessor.java b/src/main/java/org/springframework/persistence/graph/neo4j/OneToNRelationshipFieldAccessor.java new file mode 100644 index 000000000..b1fbdf81d --- /dev/null +++ b/src/main/java/org/springframework/persistence/graph/neo4j/OneToNRelationshipFieldAccessor.java @@ -0,0 +1,81 @@ +package org.springframework.persistence.graph.neo4j; + +import java.util.HashSet; +import java.util.Set; + +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.persistence.graph.neo4j.ManagedFieldAccessorSet; +import org.springframework.persistence.support.EntityInstantiator; + +public class OneToNRelationshipFieldAccessor implements FieldAccessor { + + private final RelationshipType type; + private final Direction direction; + private final Class relatedType; + private final EntityInstantiator graphEntityInstantiator; + + public OneToNRelationshipFieldAccessor(RelationshipType type, Direction direction, Class elementClass, EntityInstantiator graphEntityInstantiator) { + this.type = type; + this.direction = direction; + this.relatedType = elementClass; + this.graphEntityInstantiator = graphEntityInstantiator; + } + + public Object apply(final NodeBacked entity, final Object newVal) { + Node entityNode = entity.getUnderlyingNode(); + + Set newNodes=new HashSet(); + if (newVal != null) { + if (!(newVal instanceof Set)) { + throw new IllegalArgumentException("New value must be a Set, was: " + newVal.getClass()); + } + Set set = (Set) newVal; + for (Object obj : set) { + if (!(obj instanceof NodeBacked)) { + throw new IllegalArgumentException("New value elements must be NodeBacked."); + } + Node newNode=((NodeBacked) obj).getUnderlyingNode(); + if (entityNode.equals(newNode)) { + throw new InvalidDataAccessApiUsageException("Cannot create circular reference."); + } + newNodes.add(newNode); + } + } + for ( Relationship relationship : entityNode.getRelationships(type, direction) ) { + if (!newNodes.remove(relationship.getOtherNode(entityNode))) + relationship.delete(); + } + if (newVal == null) { + return null; + } + + for (Node newNode : newNodes) { + switch(direction) { + case OUTGOING : entityNode.createRelationshipTo(newNode, type); break; + case INCOMING : newNode.createRelationshipTo(entityNode, type); break; + default : throw new IllegalArgumentException("invalid direction " + direction); + } + } + return new ManagedFieldAccessorSet(entity, newVal, this); + } + + @Override + public Object readObject(NodeBacked entity) { + Node entityNode = entity.getUnderlyingNode(); + if (entityNode == null) { + throw new IllegalStateException("Entity must have a backing Node"); + } + Set result = new HashSet(); + for (Relationship rel : entityNode.getRelationships(type, direction)) { + result.add(graphEntityInstantiator.createEntityFromState(rel.getOtherNode(entityNode), relatedType)); + } + return new ManagedFieldAccessorSet(entity, result, this); + } + +} + + \ No newline at end of file diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/ReadOnlyOneToNRelationshipFieldAccessor.java b/src/main/java/org/springframework/persistence/graph/neo4j/ReadOnlyOneToNRelationshipFieldAccessor.java new file mode 100644 index 000000000..5ac687562 --- /dev/null +++ b/src/main/java/org/springframework/persistence/graph/neo4j/ReadOnlyOneToNRelationshipFieldAccessor.java @@ -0,0 +1,44 @@ +package org.springframework.persistence.graph.neo4j; + +import java.util.HashSet; +import java.util.Set; + +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.persistence.support.EntityInstantiator; + +public class ReadOnlyOneToNRelationshipFieldAccessor implements FieldAccessor { + + private final RelationshipType type; + private final Direction direction; + private final Class relatedType; + private final EntityInstantiator graphEntityInstantiator; + + public ReadOnlyOneToNRelationshipFieldAccessor(RelationshipType type, Direction direction, Class elementClass, EntityInstantiator graphEntityInstantiator) { + this.type = type; + this.direction = direction; + this.relatedType = elementClass; + this.graphEntityInstantiator = graphEntityInstantiator; + } + + public Object apply(final NodeBacked entity, final Object newVal) { + throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field."); + } + + @Override + public Object readObject(NodeBacked entity) { + Node entityNode = entity.getUnderlyingNode(); + if (entityNode == null) { + throw new IllegalStateException("Entity must have a backing Node"); + } + Set result = new HashSet(); + for (Relationship rel : entityNode.getRelationships(type, direction)) { + result.add(graphEntityInstantiator.createEntityFromState(rel.getOtherNode(entityNode), relatedType)); + } + return new ManagedFieldAccessorSet(entity, result, this); + } + +} diff --git a/src/main/java/org/springframework/persistence/graph/neo4j/SingleRelationshipFieldAccessor.java b/src/main/java/org/springframework/persistence/graph/neo4j/SingleRelationshipFieldAccessor.java new file mode 100644 index 000000000..5ecd241b6 --- /dev/null +++ b/src/main/java/org/springframework/persistence/graph/neo4j/SingleRelationshipFieldAccessor.java @@ -0,0 +1,62 @@ +package org.springframework.persistence.graph.neo4j; + +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.persistence.support.EntityInstantiator; + +public class SingleRelationshipFieldAccessor implements FieldAccessor { + + private final RelationshipType type; + private final Direction direction; + private final Class relatedType; + private final EntityInstantiator graphEntityInstantiator; + + public SingleRelationshipFieldAccessor(RelationshipType type, Direction direction, Class clazz, EntityInstantiator graphEntityInstantiator) { + this.type = type; + this.direction = direction; + this.relatedType = clazz; + this.graphEntityInstantiator = graphEntityInstantiator; + } + + public Object apply(NodeBacked entity, Object newVal) { + if (newVal != null && !(newVal instanceof NodeBacked)) { + throw new IllegalArgumentException("New value must be NodeBacked."); + } + Node entityNode = entity.getUnderlyingNode(); + for ( Relationship relationship : entityNode.getRelationships(type, direction) ) { + relationship.delete(); + } + if (newVal == null) { + return null; + } + Node targetNode = ((NodeBacked) newVal).getUnderlyingNode(); + if (entityNode.equals(targetNode)) { + throw new InvalidDataAccessApiUsageException("Cannot create circular reference."); + } + switch(direction) { + case OUTGOING : entityNode.createRelationshipTo(targetNode, type); break; + case INCOMING : targetNode.createRelationshipTo(entityNode, type); break; + default : throw new IllegalArgumentException("invalid direction " + direction); + } + return newVal; + } + + @Override + public Object readObject(NodeBacked entity) { + Node entityNode = entity.getUnderlyingNode(); + if (entityNode == null) { + throw new IllegalStateException("Entity must have a backing Node"); + } + Relationship singleRelationship = entityNode.getSingleRelationship(type, direction); + + if (singleRelationship == null) { + return null; + } + Node targetNode = singleRelationship.getOtherNode(entityNode); + return graphEntityInstantiator.createEntityFromState(targetNode, relatedType); + } + +} \ No newline at end of file