diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeBacked.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeBacked.java index bde63c6f8..bfbc40926 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeBacked.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/core/NodeBacked.java @@ -93,7 +93,7 @@ public interface NodeBacked extends GraphBacked { * @return Lazy {@link java.lang.Iterable} over the traversal results, converted to the expected node * entity instances */ - Iterable findAllByTraversal(final Class targetType, TraversalDescription traversalDescription); + Iterable findAllByTraversal(final Class targetType, TraversalDescription traversalDescription); /** * Perform a traversal from this entity's underlying node with the given traversal description. The found paths diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java index 0cd9565a1..da36e4063 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/fieldaccess/TraversalFieldAccessorFactory.java @@ -16,13 +16,15 @@ package org.springframework.data.neo4j.fieldaccess; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Path; +import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.traversal.TraversalDescription; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.neo4j.annotation.GraphTraversal; -import org.springframework.data.neo4j.annotation.Query; import org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder; import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.repository.DirectGraphRepositoryFactory; +import org.springframework.data.neo4j.core.RelationshipBacked; import org.springframework.data.neo4j.support.GenericTypeExtractor; import java.lang.reflect.Constructor; @@ -52,7 +54,7 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory { protected final Field field; private final FieldTraversalDescriptionBuilder fieldTraversalDescriptionBuilder; - private Class target; + private Class target; protected String[] params; public TraversalFieldAccessor(final Field field) { @@ -63,15 +65,23 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory resolveTarget(GraphTraversal graphTraversal, Field field) { - if (!graphTraversal.elementClass().equals(Object.class)) return graphTraversal.elementClass(); + private Class resolveTarget(GraphTraversal graphTraversal, Field field) { + if (!graphTraversal.elementClass().equals(NodeBacked.class)) return graphTraversal.elementClass(); final Class result = GenericTypeExtractor.resolveFieldType(field); - if (!NodeBacked.class.isAssignableFrom(result)) throw new IllegalArgumentException("The target result type of the traversal is no node entity: "+field); - return (Class) result; + Class[] allowedTypes={NodeBacked.class,RelationshipBacked.class,Node.class,Relationship.class, Path.class}; + if (!checkTypes(result,allowedTypes)) throw new IllegalArgumentException("The target result type "+result+" of the traversal is no subclass of the allowed types: "+field+" "+allowedTypes); + return result; + } + + private boolean checkTypes(Class target, Class...allowedTypes) { + for (Class type : allowedTypes) { + if (type.isAssignableFrom(target)) return true; + } + return false; } - @Override + @Override public boolean isWriteable(NodeBacked nodeBacked) { return false; } @@ -84,7 +94,7 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory) target, traversalDescription)); + return doReturn(nodeBacked.findAllByTraversal(target, traversalDescription)); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj index 94f3a35c7..9e31a1997 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/Neo4jNodeBacking.aj @@ -21,6 +21,8 @@ import org.apache.commons.logging.LogFactory; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.FieldSignature; import org.neo4j.graphdb.DynamicRelationshipType; +import org.neo4j.graphdb.Path; +import org.neo4j.helpers.collection.IterableWrapper; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.traversal.TraversalDescription; @@ -180,10 +182,35 @@ public aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMixinFields Iterable NodeBacked.findAllByTraversal(final Class targetType, TraversalDescription traversalDescription) { + public Iterable NodeBacked.findAllByTraversal(final Class targetType, TraversalDescription traversalDescription) { if (!hasPersistentState()) throw new IllegalStateException("No node attached to " + this); final Traverser traverser = traversalDescription.traverse(this.getPersistentState()); - return new NodeBackedNodeIterableWrapper(traverser, targetType, Neo4jNodeBacking.aspectOf().graphDatabaseContext); + if (Node.class.isAssignableFrom(targetType)) return (Iterable) traverser.nodes(); + if (Relationship.class.isAssignableFrom(targetType)) return (Iterable) traverser.relationships(); + if (Path.class.isAssignableFrom(targetType)) return (Iterable) traverser; + return (Iterable)Neo4jNodeBacking.aspectOf().convertToGraphEntity(traverser,targetType); + } + + private Iterable convertToGraphEntity(Traverser traverser, final Class targetType) { + final GraphDatabaseContext ctx = Neo4jNodeBacking.aspectOf().graphDatabaseContext; + if (NodeBacked.class.isAssignableFrom(targetType)) { + return new IterableWrapper(traverser.nodes()) { + @Override + protected Object underlyingObjectToObject(Node node) { + return ctx.createEntityFromState(node,(Class)targetType); + } + }; + } + if (RelationshipBacked.class.isAssignableFrom(targetType)) { + return new IterableWrapper(traverser.relationships()) { + @Override + protected Object underlyingObjectToObject(Relationship relationship) { + return ctx.createEntityFromState(relationship,(Class)targetType); + } + }; + } + throw new IllegalStateException("Can't determine valid type for traversal target "+targetType); + } public Iterable NodeBacked.findAllByQuery(final String query, final Class targetType, Map params) { diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeBackedNodeIterableWrapper.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeBackedNodeIterableWrapper.java deleted file mode 100644 index d86f1bf0f..000000000 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/support/node/NodeBackedNodeIterableWrapper.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2011 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.data.neo4j.support.node; - -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.traversal.Traverser; -import org.neo4j.helpers.collection.IterableWrapper; -import org.springframework.data.neo4j.core.NodeBacked; -import org.springframework.data.neo4j.support.GraphDatabaseContext; - -/** - * Simple wrapper to create an Iterable from a traverser, tied to a NodeBacked entity. Creates NodeEntities on the fly - * while iterating the Iterator from the traverersal result. - * @author Michael Hunger - * @since 14.09.2010 - */ -public class NodeBackedNodeIterableWrapper extends IterableWrapper { - private final Class targetType; - private final GraphDatabaseContext graphDatabaseContext; - - public NodeBackedNodeIterableWrapper(Traverser traverser, Class targetType, final GraphDatabaseContext graphDatabaseContext) { - super(traverser.nodes()); - this.targetType = targetType; - this.graphDatabaseContext = graphDatabaseContext; - } - - @Override - protected T underlyingObjectToObject(Node node) { - return graphDatabaseContext.createEntityFromState(node, targetType); - } -}