refactoring fieldaccessors to be used independently

This commit is contained in:
Michael Hunger
2010-09-12 16:21:09 +02:00
parent 8896aab27b
commit 9a073f38a7
15 changed files with 614 additions and 172 deletions

View File

@@ -0,0 +1,67 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.core.convert.ConversionService;
import org.springframework.datastore.graph.api.NodeBacked;
import java.lang.reflect.Field;
/**
* @author Michael Hunger
* @since 12.09.2010
*/
@Configurable
public class ConvertingNodePropertyFieldAccessor extends NodePropertyFieldAccessor {
private final ConversionService conversionService;
public ConvertingNodePropertyFieldAccessor(final Field field, final ConversionService conversionService) {
super(field);
this.conversionService = conversionService;
}
@Override
public Object setValue(final NodeBacked nodeBacked, final Object newVal) {
super.setValue(nodeBacked,serializePropertyValue(newVal));
return newVal;
}
@Override
public Object getValue(final NodeBacked nodeBacked) {
return deserializePropertyValue(super.getValue(nodeBacked));
}
private Object serializePropertyValue(final Object newVal) {
return conversionService.convert(newVal, String.class);
}
private Object deserializePropertyValue(final Object value) {
return conversionService.convert(value, field.getType());
}
public static FieldAccessorFactory<NodeBacked> factory() {
return new FieldAccessorFactory<NodeBacked>() {
@Autowired
ConversionService conversionService;
@Override
public boolean accept(final Field field) {
return isSerializableField(field) && isDeserializableField(field);
}
@Override
public FieldAccessor<NodeBacked,?> forField(final Field field) {
return new ConvertingNodePropertyFieldAccessor(field,conversionService);
}
private boolean isSerializableField(final Field field) {
return !DelegatingFieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(field.getType(), String.class);
}
private boolean isDeserializableField(final Field field) {
return !DelegatingFieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(String.class, field.getType());
}
};
}
}

View File

@@ -0,0 +1,152 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.springframework.datastore.graph.api.*;
import org.springframework.persistence.support.EntityInstantiator;
public class DelegatingFieldAccessorFactory<T> implements FieldAccessorFactory<T> {
private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private final EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
public DelegatingFieldAccessorFactory(EntityInstantiator<NodeBacked,Node> graphEntityInstantiator, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
this.graphEntityInstantiator = graphEntityInstantiator;
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
}
@Override
public boolean accept(Field f) {
return isSingleRelationshipField(f) || isOneToNRelationshipEntityField(f) || isReadOnlyOneToNRelationshipField(f) || isOneToNRelationshipField(f);
}
final Collection<FieldAccessorFactory<?>> fieldAccessorFactories = Arrays.<FieldAccessorFactory<?>>asList(
NodePropertyFieldAccessor.factory(),
ConvertingNodePropertyFieldAccessor.factory(),
SingleRelationshipFieldAccessor.factory(),
OneToNRelationshipFieldAccessor.factory(),
ReadOnlyOneToNRelationshipFieldAccessor.factory(),
OneToNRelationshipEntityFieldAccessor.factory()
);
final Collection<FieldAccessorListenerFactory<?>> fieldAccessorListenerFactories = Arrays.<FieldAccessorListenerFactory<?>>asList(
IndexingNodePropertyFieldAccessorListener.factory()
);
public FieldAccessor forField(Field field) {
if (Modifier.isTransient(field.getModifiers())) return null;
GraphEntityRelationship relAnnotation = field.getAnnotation(GraphEntityRelationship.class);
if (isSingleRelationshipField(field)) {
if (relAnnotation != null) {
return new SingleRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(field), graphEntityInstantiator);
}
return new SingleRelationshipFieldAccessor(typeFrom(field), Direction.OUTGOING, targetFrom(field), graphEntityInstantiator);
}
if (isOneToNRelationshipField(field)) {
return new OneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
}
if (isReadOnlyOneToNRelationshipField(field)) {
return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
}
if (isOneToNRelationshipEntityField(field)) {
GraphEntityRelationshipEntity relEntityAnnotation = field.getAnnotation(GraphEntityRelationshipEntity.class);
return new OneToNRelationshipEntityFieldAccessor(typeFrom(relEntityAnnotation), dirFrom(relEntityAnnotation), targetFrom(relEntityAnnotation), relationshipEntityInstantiator);
}
throw new IllegalArgumentException("Not a Neo4j relationship field: " + field);
}
private Class<? extends NodeBacked> targetFrom(Field field) {
return (Class<? extends NodeBacked>) field.getType();
}
private Class<? extends NodeBacked> targetFrom(GraphEntityRelationship relAnnotation) {
return relAnnotation.elementClass();
}
private Direction dirFrom(GraphEntityRelationship relAnnotation) {
return relAnnotation.direction().toNeo4jDir();
}
private DynamicRelationshipType typeFrom(Field field) {
return DynamicRelationshipType.withName(getNeo4jPropertyName(field));
}
private Class<? extends RelationshipBacked> targetFrom(GraphEntityRelationshipEntity relEntityAnnotation) {
return relEntityAnnotation.elementClass();
}
private Direction dirFrom(GraphEntityRelationshipEntity relEntityAnnotation) {
return relEntityAnnotation.direction().toNeo4jDir();
}
private DynamicRelationshipType typeFrom(GraphEntityRelationshipEntity relEntityAnnotation) {
return DynamicRelationshipType.withName(relEntityAnnotation.type());
}
private DynamicRelationshipType typeFrom(GraphEntityRelationship relAnnotation) {
return DynamicRelationshipType.withName(relAnnotation.type());
}
public static boolean isRelationshipField(Field f) {
return isSingleRelationshipField(f)
|| isOneToNRelationshipField(f)
|| isOneToNRelationshipEntityField(f)
|| isReadOnlyOneToNRelationshipField(f);
}
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;
GraphEntityRelationship relAnnotation = f.getAnnotation(GraphEntityRelationship.class);
return relAnnotation != null && NodeBacked.class.isAssignableFrom(relAnnotation.elementClass()) && !relAnnotation.elementClass().equals(NodeBacked.class);
}
private static boolean isReadOnlyOneToNRelationshipField(Field f) {
GraphEntityRelationship relAnnotation = f.getAnnotation(GraphEntityRelationship.class);
return Iterable.class.equals(f.getType())
&& relAnnotation != null
&& !NodeBacked.class.equals(relAnnotation.elementClass());
}
private static boolean isOneToNRelationshipEntityField(Field f) {
GraphEntityRelationshipEntity relEntityAnnotation = f.getAnnotation(GraphEntityRelationshipEntity.class);
return Iterable.class.isAssignableFrom(f.getType())
&& relEntityAnnotation != null
&& !RelationshipBacked.class.equals(relEntityAnnotation.elementClass());
}
public static String getNeo4jPropertyName(Field field) {
final Class<?> entityClass = field.getDeclaringClass();
if (useShortNames(entityClass)) return field.getName();
return String.format("%s.%s", entityClass.getSimpleName(), field.getName());
}
private static boolean useShortNames(Class<?> entityClass) {
final GraphEntity graphEntity = entityClass.getAnnotation(GraphEntity.class);
if (graphEntity!=null) return graphEntity.useShortNames();
final GraphRelationship graphRelationship = entityClass.getAnnotation(GraphRelationship.class);
if (graphRelationship!=null) return graphRelationship.useShortNames();
return false;
}
public List<FieldAccessor<T, ?>> listenersFor(Field field) {
List<FieldAccessor<T,?>> result=new ArrayList<FieldAccessor<T,?>>();
for (FieldAccessorListenerFactory<?> fieldAccessorListenerFactory : fieldAccessorListenerFactories) {
if (fieldAccessorListenerFactory.accept(field)) {
final FieldAccessor<T, ?> listener = (FieldAccessor<T, ?>) fieldAccessorListenerFactory.forField(field);
result.add(listener);
}
}
return result;
}
}

View File

@@ -0,0 +1,60 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.api.RelationshipBacked;
import org.springframework.persistence.support.EntityInstantiator;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Michael Hunger
* @since 12.09.2010
*/
public class EntityStateAccessors<ENTITY, STATE> {
private final STATE underlyingState;
private final ENTITY entity;
private final Class<? extends ENTITY> type;
private final Map<Field,FieldAccessor<ENTITY,?>> fieldAccessors=new HashMap<Field, FieldAccessor<ENTITY,?>>();
private final Map<Field,List<FieldAccessListener<ENTITY,?>>> fieldAccessorListeners=new HashMap<Field, List<FieldAccessListener<ENTITY,?>>>();
public EntityStateAccessors(final STATE underlyingState, final ENTITY entity, final Class<? extends ENTITY> type, final EntityInstantiator<NodeBacked, Node> nodeInstantiator, final EntityInstantiator<RelationshipBacked, Relationship> relationshipInstantiator) {
this.underlyingState = underlyingState;
this.entity = entity;
this.type = type;
createAccessorsAndListeners(type, nodeInstantiator, relationshipInstantiator);
}
private void createAccessorsAndListeners(final Class<? extends ENTITY> type, final EntityInstantiator<NodeBacked, Node> nodeInstantiator, final EntityInstantiator<RelationshipBacked, Relationship> relationshipInstantiator) {
final DelegatingFieldAccessorFactory fieldAccessorFactory = new DelegatingFieldAccessorFactory(nodeInstantiator,relationshipInstantiator);
ReflectionUtils.doWithFields(type, new ReflectionUtils.FieldCallback() {
public void doWith(final Field field) throws IllegalArgumentException, IllegalAccessException {
fieldAccessors.put(field, fieldAccessorFactory.forField(field));
fieldAccessorListeners.put(field, fieldAccessorFactory.listenersFor(field)); // TODO Bad code
}
});
}
public Object getValue(final Field field) {
return fieldAccessors.get(field).getValue(entity);
}
public Object setValue(final Field field, final Object newVal) {
final Object result = fieldAccessors.get(field).setValue(entity, newVal);
notifyListeners(field, result); // async ?
return result;
}
private void notifyListeners(Field field, Object result) {
if (fieldAccessorListeners.containsKey(field)) {
for (final FieldAccessListener<ENTITY, ?> listener : fieldAccessorListeners.get(field)) {
listener.valueChanged(entity,null,result); // todo oldValue
}
}
}
}

View File

@@ -0,0 +1,7 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
public interface FieldAccessListener<ENTITY, TARGET> {
void valueChanged(ENTITY entity, Object oldVal, Object newVal);
}

View File

@@ -1,121 +1,12 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
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.datastore.graph.api.*;
import org.springframework.persistence.support.EntityInstantiator;
public class FieldAccessorFactory {
private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private final EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
public FieldAccessorFactory(EntityInstantiator<NodeBacked,Node> graphEntityInstantiator, EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
this.graphEntityInstantiator = graphEntityInstantiator;
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
}
public FieldAccessor forField(Field field) {
if (Modifier.isTransient(field.getModifiers())) return null;
GraphEntityRelationship relAnnotation = field.getAnnotation(GraphEntityRelationship.class);
if (isSingleRelationshipField(field)) {
if (relAnnotation != null) {
return new SingleRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(field), graphEntityInstantiator);
}
return new SingleRelationshipFieldAccessor(typeFrom(field), Direction.OUTGOING, targetFrom(field), graphEntityInstantiator);
}
if (isOneToNRelationshipField(field)) {
return new OneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
}
if (isReadOnlyOneToNRelationshipField(field)) {
return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
}
if (isOneToNRelationshipEntityField(field)) {
GraphEntityRelationshipEntity relEntityAnnotation = field.getAnnotation(GraphEntityRelationshipEntity.class);
return new OneToNRelationshipEntityFieldAccessor(typeFrom(relEntityAnnotation), dirFrom(relEntityAnnotation), targetFrom(relEntityAnnotation), relationshipEntityInstantiator);
}
throw new IllegalArgumentException("Not a Neo4j relationship field: " + field);
}
private Class<? extends RelationshipBacked> targetFrom(GraphEntityRelationshipEntity relEntityAnnotation) {
return relEntityAnnotation.elementClass();
}
private Direction dirFrom(GraphEntityRelationshipEntity relEntityAnnotation) {
return relEntityAnnotation.direction().toNeo4jDir();
}
private Class<? extends NodeBacked> targetFrom(Field field) {
return (Class<? extends NodeBacked>) field.getType();
}
private Class<? extends NodeBacked> targetFrom(GraphEntityRelationship relAnnotation) {
return relAnnotation.elementClass();
}
private Direction dirFrom(GraphEntityRelationship relAnnotation) {
return relAnnotation.direction().toNeo4jDir();
}
private DynamicRelationshipType typeFrom(Field field) {
return DynamicRelationshipType.withName(getNeo4jPropertyName(field));
}
private DynamicRelationshipType typeFrom(GraphEntityRelationshipEntity relEntityAnnotation) {
return DynamicRelationshipType.withName(relEntityAnnotation.type());
}
private DynamicRelationshipType typeFrom(GraphEntityRelationship relAnnotation) {
return DynamicRelationshipType.withName(relAnnotation.type());
}
public static boolean isRelationshipField(Field f) {
return isSingleRelationshipField(f)
|| isOneToNRelationshipField(f)
|| isOneToNRelationshipEntityField(f)
|| isReadOnlyOneToNRelationshipField(f);
}
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;
GraphEntityRelationship relAnnotation = f.getAnnotation(GraphEntityRelationship.class);
return relAnnotation != null && NodeBacked.class.isAssignableFrom(relAnnotation.elementClass()) && !relAnnotation.elementClass().equals(NodeBacked.class);
}
private static boolean isReadOnlyOneToNRelationshipField(Field f) {
GraphEntityRelationship relAnnotation = f.getAnnotation(GraphEntityRelationship.class);
return Iterable.class.equals(f.getType())
&& relAnnotation != null
&& !NodeBacked.class.equals(relAnnotation.elementClass());
}
private static boolean isOneToNRelationshipEntityField(Field f) {
GraphEntityRelationshipEntity relEntityAnnotation = f.getAnnotation(GraphEntityRelationshipEntity.class);
return Iterable.class.isAssignableFrom(f.getType())
&& relEntityAnnotation != null
&& !RelationshipBacked.class.equals(relEntityAnnotation.elementClass());
}
public static String getNeo4jPropertyName(Field field) {
final Class<?> entityClass = field.getDeclaringClass();
if (useShortNames(entityClass)) return field.getName();
return String.format("%s.%s", entityClass.getSimpleName(), field.getName());
}
private static boolean useShortNames(Class<?> entityClass) {
final GraphEntity graphEntity = entityClass.getAnnotation(GraphEntity.class);
if (graphEntity!=null) return graphEntity.useShortNames();
final GraphRelationship graphRelationship = entityClass.getAnnotation(GraphRelationship.class);
if (graphRelationship!=null) return graphRelationship.useShortNames();
return false;
}
}
/**
* @author Michael Hunger
* @since 12.09.2010
*/
interface FieldAccessorFactory<E> {
boolean accept(Field f);
FieldAccessor<E,?> forField(Field f);
}

View File

@@ -0,0 +1,13 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import java.lang.reflect.Field;
/**
* @author Michael Hunger
* @since 12.09.2010
*/
interface FieldAccessorListenerFactory<E> {
boolean accept(Field f);
FieldAccessListener<E, ?> forField(Field f);
}

View File

@@ -0,0 +1,49 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import org.neo4j.index.IndexService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.datastore.graph.api.NodeBacked;
import java.lang.reflect.Field;
/**
* @author Michael Hunger
* @since 12.09.2010
*/
public class IndexingNodePropertyFieldAccessorListener implements FieldAccessListener<NodeBacked, Object> {
protected final Field field;
private final IndexService indexService;
public IndexingNodePropertyFieldAccessorListener(final Field field, final IndexService indexService) {
this.field = field;
this.indexService = indexService;
}
@Override
public void valueChanged(final NodeBacked nodeBacked, final Object oldVal, final Object newVal) {
if (newVal==null) indexService.removeIndex(nodeBacked.getUnderlyingNode(),getPropertyName());
else indexService.index(nodeBacked.getUnderlyingNode(), getPropertyName(),newVal);
}
// todo
private String getPropertyName() {
return DelegatingFieldAccessorFactory.getNeo4jPropertyName(field);
}
public static FieldAccessorListenerFactory<NodeBacked> factory() {
return new FieldAccessorListenerFactory<NodeBacked>() {
@Autowired
IndexService indexService;
@Override
public boolean accept(final Field f) {
return NodePropertyFieldAccessor.factory().accept(f) || ConvertingNodePropertyFieldAccessor.factory().accept(f);
}
@Override
public FieldAccessListener<NodeBacked,?> forField(final Field field) {
return new IndexingNodePropertyFieldAccessorListener(field,indexService);
}
};
}
}

View File

@@ -0,0 +1,57 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import org.springframework.datastore.graph.api.NodeBacked;
import java.lang.reflect.Field;
/**
* @author Michael Hunger
* @since 12.09.2010
*/
public class NodePropertyFieldAccessor implements FieldAccessor<NodeBacked, Object> {
protected final Field field;
public NodePropertyFieldAccessor(final Field field) {
this.field = field;
}
@Override
public Object setValue(final NodeBacked nodeBacked, final Object newVal) {
nodeBacked.getUnderlyingNode().setProperty(getPropertyName(),newVal);
return newVal;
}
@Override
public Object getValue(final NodeBacked nodeBacked) {
return nodeBacked.getUnderlyingNode().getProperty(getPropertyName());
}
private String getPropertyName() {
return DelegatingFieldAccessorFactory.getNeo4jPropertyName(field);
}
public static FieldAccessorFactory<NodeBacked> factory() {
return new FieldAccessorFactory<NodeBacked>() {
@Override
public boolean accept(final Field f) {
return isNeo4jPropertyType(f.getType());
}
@Override
public FieldAccessor<NodeBacked,?> forField(final Field field) {
return new NodePropertyFieldAccessor(field);
}
private boolean isNeo4jPropertyType(final Class<?> fieldType) {
// todo: add array support
return fieldType.isPrimitive()
|| (fieldType.isArray() && !fieldType.getComponentType().isArray() && isNeo4jPropertyType(fieldType.getComponentType()))
|| fieldType.equals(String.class)
|| fieldType.equals(Character.class)
|| fieldType.equals(Boolean.class)
|| (fieldType.getName().startsWith("java.lang") && Number.class.isAssignableFrom(fieldType));
}
};
}
}

View File

@@ -1,34 +1,34 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
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.neo4j.graphdb.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.datastore.graph.api.GraphEntityRelationshipEntity;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.api.RelationshipBacked;
import org.springframework.persistence.support.EntityInstantiator;
public class OneToNRelationshipEntityFieldAccessor extends AbstractRelationshipFieldAccessor<NodeBacked, Node, RelationshipBacked,Relationship> {
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
public OneToNRelationshipEntityFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends RelationshipBacked> elementClass, final EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
super(elementClass,relationshipEntityInstantiator,direction,type);
}
public class OneToNRelationshipEntityFieldAccessor extends AbstractRelationshipFieldAccessor<NodeBacked, Node, RelationshipBacked, Relationship> {
@Override
public Object setValue(final NodeBacked entity, final Object newVal) {
throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field.");
}
public OneToNRelationshipEntityFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends RelationshipBacked> elementClass, final EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator) {
super(elementClass, relationshipEntityInstantiator, direction, type);
}
@Override
public Object getValue(final NodeBacked entity) {
@Override
public Object setValue(final NodeBacked entity, final Object newVal) {
throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field.");
}
@Override
public Object getValue(final NodeBacked entity) {
checkUnderlyingNode(entity);
final Set<RelationshipBacked> result = createEntitySetFromRelationships(entity);
return new ManagedFieldAccessorSet<NodeBacked, RelationshipBacked>(entity, result, this);
}
return new ManagedFieldAccessorSet<NodeBacked, RelationshipBacked>(entity, result, this);
}
private Set<RelationshipBacked> createEntitySetFromRelationships(final NodeBacked entity) {
final Set<RelationshipBacked> result = new HashSet<RelationshipBacked>();
@@ -39,17 +39,59 @@ public class OneToNRelationshipEntityFieldAccessor extends AbstractRelationshipF
}
@Override
protected Iterable<Relationship> getStatesFromEntity(NodeBacked entity) {
protected Iterable<Relationship> getStatesFromEntity(final NodeBacked entity) {
return entity.getUnderlyingNode().getRelationships(type, direction);
}
@Override
protected Relationship obtainSingleRelationship(Node start, Relationship end) {
protected Relationship obtainSingleRelationship(final Node start, final Relationship end) {
return null;
}
@Override
protected Node getState(NodeBacked nodeBacked) {
protected Node getState(final NodeBacked nodeBacked) {
return nodeBacked.getUnderlyingNode();
}
public static FieldAccessorFactory<NodeBacked> factory() {
return new RelationshipEntityFieldAccessorFactory();
}
private static class RelationshipEntityFieldAccessorFactory implements FieldAccessorFactory<NodeBacked> {
@Autowired
private EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
@Override
public boolean accept(final Field f) {
return Iterable.class.isAssignableFrom(f.getType()) && hasValidRelationshipAnnotation(f);
}
@Override
public FieldAccessor<NodeBacked, ?> forField(final Field field) {
final GraphEntityRelationshipEntity relEntityAnnotation = getRelationshipAnnotation(field);
return new OneToNRelationshipEntityFieldAccessor(typeFrom(relEntityAnnotation), dirFrom(relEntityAnnotation), targetFrom(relEntityAnnotation), relationshipEntityInstantiator);
}
private boolean hasValidRelationshipAnnotation(final Field f) {
final GraphEntityRelationshipEntity relEntityAnnotation = getRelationshipAnnotation(f);
return relEntityAnnotation != null && !RelationshipBacked.class.equals(relEntityAnnotation.elementClass());
}
private GraphEntityRelationshipEntity getRelationshipAnnotation(final Field field) {
return field.getAnnotation(GraphEntityRelationshipEntity.class);
}
private Class<? extends RelationshipBacked> targetFrom(final GraphEntityRelationshipEntity relEntityAnnotation) {
return relEntityAnnotation.elementClass();
}
private Direction dirFrom(final GraphEntityRelationshipEntity relEntityAnnotation) {
return relEntityAnnotation.direction().toNeo4jDir();
}
private DynamicRelationshipType typeFrom(final GraphEntityRelationshipEntity relEntityAnnotation) {
return DynamicRelationshipType.withName(relEntityAnnotation.type());
}
}
}

View File

@@ -1,39 +1,58 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import java.util.Collections;
import java.util.Set;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType;
import org.springframework.datastore.graph.api.GraphEntityRelationship;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.persistence.support.EntityInstantiator;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
public class OneToNRelationshipFieldAccessor extends NodeToNodesRelationshipFieldAccessor<NodeBacked> {
public OneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> elementClass, final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
public OneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> elementClass, final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
super(elementClass, graphEntityInstantiator, direction, type);
}
}
public Object setValue(final NodeBacked entity, final Object newVal) {
public Object setValue(final NodeBacked entity, final Object newVal) {
final Node node = checkUnderlyingNode(entity);
if (newVal==null) {
if (newVal == null) {
removeMissingRelationships(node, Collections.<Node>emptySet());
return null;
}
final Set<Node> targetNodes = checkTargetIsSetOfNodebacked(newVal);
checkNoCircularReference(node,targetNodes);
checkNoCircularReference(node, targetNodes);
removeMissingRelationships(node, targetNodes);
createAddedRelationships(node,targetNodes);
return createManagedSet(entity, (Set<NodeBacked>)newVal);
}
createAddedRelationships(node, targetNodes);
return createManagedSet(entity, (Set<NodeBacked>) newVal);
}
@Override
public Object getValue(final NodeBacked entity) {
public Object getValue(final NodeBacked entity) {
checkUnderlyingNode(entity);
final Set<NodeBacked> result = createEntitySetFromRelationshipEndNodes(entity);
return createManagedSet(entity, result);
}
return createManagedSet(entity, result);
}
public static FieldAccessorFactory<NodeBacked> factory() {
return new RelationshipFieldAccessorFactory() {
@Override
public boolean accept(final Field f) {
return Collection.class.isAssignableFrom(f.getType()) && hasValidRelationshipAnnotation(f);
}
@Override
public FieldAccessor<NodeBacked, ?> forField(final Field field) {
final GraphEntityRelationship relAnnotation = getRelationshipAnnotation(field);
return new OneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
}
};
}
}

View File

@@ -1,23 +1,39 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import java.util.HashSet;
import java.util.Set;
import java.lang.reflect.Field;
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.datastore.graph.api.GraphEntityRelationship;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.persistence.support.EntityInstantiator;
public class ReadOnlyOneToNRelationshipFieldAccessor extends OneToNRelationshipFieldAccessor {
public ReadOnlyOneToNRelationshipFieldAccessor(RelationshipType type, Direction direction, Class<? extends NodeBacked> elementClass, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
public ReadOnlyOneToNRelationshipFieldAccessor(final RelationshipType type, final Direction direction, final Class<? extends NodeBacked> elementClass, final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
super(type,direction,elementClass,graphEntityInstantiator);
}
public Object setValue(final NodeBacked entity, final Object newVal) {
throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field.");
}
public static FieldAccessorFactory<NodeBacked> factory() {
return new RelationshipFieldAccessorFactory() {
@Override
public boolean accept(final Field f) {
return Iterable.class.equals(f.getType()) && hasValidRelationshipAnnotation(f);
}
@Override
public FieldAccessor<NodeBacked, ?> forField(final Field field) {
final GraphEntityRelationship relAnnotation = getRelationshipAnnotation(field);
return new ReadOnlyOneToNRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(relAnnotation), graphEntityInstantiator);
}
};
}
}

View File

@@ -0,0 +1,51 @@
package org.springframework.datastore.graph.neo4j.fieldaccess;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.datastore.graph.api.GraphEntityRelationship;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.persistence.support.EntityInstantiator;
import java.lang.reflect.Field;
/**
* @author Michael Hunger
* @since 12.09.2010
*/
@Configurable
abstract class RelationshipFieldAccessorFactory implements FieldAccessorFactory<NodeBacked> {
@Autowired
protected EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
protected Class<? extends NodeBacked> targetFrom(Field field) {
return (Class<? extends NodeBacked>) field.getType();
}
protected Class<? extends NodeBacked> targetFrom(GraphEntityRelationship relAnnotation) {
return (Class<? extends NodeBacked>) relAnnotation.elementClass();
}
protected Direction dirFrom(GraphEntityRelationship relAnnotation) {
return relAnnotation.direction().toNeo4jDir();
}
protected DynamicRelationshipType typeFrom(Field field) {
return DynamicRelationshipType.withName(DelegatingFieldAccessorFactory.getNeo4jPropertyName(field));
}
protected DynamicRelationshipType typeFrom(GraphEntityRelationship relAnnotation) {
return DynamicRelationshipType.withName(relAnnotation.type());
}
protected GraphEntityRelationship getRelationshipAnnotation(Field field) {
return field.getAnnotation(GraphEntityRelationship.class);
}
protected boolean hasValidRelationshipAnnotation(Field field) {
final GraphEntityRelationship relAnnotation = getRelationshipAnnotation(field);
return relAnnotation != null && !relAnnotation.elementClass().equals(NodeBacked.class);
}
}

View File

@@ -3,10 +3,11 @@ package org.springframework.datastore.graph.neo4j.fieldaccess;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.RelationshipType;
import org.springframework.datastore.graph.api.GraphEntityRelationship;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.api.RelationshipBacked;
import org.springframework.persistence.support.EntityInstantiator;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Set;
@@ -17,7 +18,7 @@ public class SingleRelationshipFieldAccessor extends NodeToNodesRelationshipFiel
@Override
public Object setValue(final NodeBacked entity, final Object newVal) {
Node node=checkUnderlyingNode(entity);
final Node node=checkUnderlyingNode(entity);
if (newVal == null) {
removeMissingRelationships(node, Collections.<Node>emptySet());
return null;
@@ -35,4 +36,22 @@ public class SingleRelationshipFieldAccessor extends NodeToNodesRelationshipFiel
final Set<NodeBacked> result = createEntitySetFromRelationshipEndNodes(entity);
return result.isEmpty() ? null : result.iterator().next();
}
public static FieldAccessorFactory<NodeBacked> factory() {
return new RelationshipFieldAccessorFactory() {
@Override
public boolean accept(final Field f) {
return NodeBacked.class.isAssignableFrom(f.getType());
}
@Override
public FieldAccessor<NodeBacked, ?> forField(final Field field) {
final GraphEntityRelationship relAnnotation = getRelationshipAnnotation(field);
if (relAnnotation == null)
return new SingleRelationshipFieldAccessor(typeFrom(field), Direction.OUTGOING, targetFrom(field), graphEntityInstantiator);
return new SingleRelationshipFieldAccessor(typeFrom(relAnnotation), dirFrom(relAnnotation), targetFrom(field), graphEntityInstantiator);
}
};
}
}

View File

@@ -19,8 +19,8 @@ import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.api.RelationshipBacked;
import org.springframework.datastore.graph.api.GraphEntity;
import org.springframework.datastore.graph.neo4j.fieldaccess.DelegatingFieldAccessorFactory;
import org.springframework.datastore.graph.neo4j.fieldaccess.FieldAccessor;
import org.springframework.datastore.graph.neo4j.fieldaccess.FieldAccessorFactory;
import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields;
import org.springframework.persistence.support.EntityInstantiator;
import org.springframework.util.ObjectUtils;
@@ -49,7 +49,7 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
public EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;
private FieldAccessorFactory fieldAccessorFactory;
private DelegatingFieldAccessorFactory fieldAccessorFactory;
public EntityInstantiator<RelationshipBacked, Relationship> relationshipEntityInstantiator;
@@ -66,7 +66,7 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
this.relationshipEntityInstantiator = relationshipEntityInstantiator;
this.indexService = indexService;
this.conversionService = conversionService;
this.fieldAccessorFactory = new FieldAccessorFactory(graphEntityInstantiator, relationshipEntityInstantiator);
this.fieldAccessorFactory = new DelegatingFieldAccessorFactory(graphEntityInstantiator, relationshipEntityInstantiator);
}
@@ -236,7 +236,7 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
if (isIdField(field)) return new ShouldProceedOrReturn(entity.getUnderlyingNode().getId());
if (Modifier.isTransient(field.getModifiers())) return new ShouldProceedOrReturn();
if (isNeo4jPropertyType(field.getType()) || isDeserializableField(field)) {
String propName = FieldAccessorFactory.getNeo4jPropertyName(field);
String propName = DelegatingFieldAccessorFactory.getNeo4jPropertyName(field);
log.info("GET " + field + " <- Neo4J simple node property [" + propName + "]");
Node node = entity.getUnderlyingNode();
Object nodeProperty = deserializePropertyValue(node.getProperty(propName, getDefaultValue(field.getType())), field.getType());
@@ -308,7 +308,7 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
if (Modifier.isFinal(field.getModifiers())) return new ShouldProceedOrReturn(newVal);
if (Modifier.isTransient(field.getModifiers())) return new ShouldProceedOrReturn(true, newVal);
if (isNeo4jPropertyType(field.getType()) || isSerializableField(field)) {
String propName = FieldAccessorFactory.getNeo4jPropertyName(field);
String propName = DelegatingFieldAccessorFactory.getNeo4jPropertyName(field);
Node node = entity.getUnderlyingNode();
if (newVal==null) {
node.removeProperty(propName);
@@ -395,11 +395,11 @@ public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEn
}
private boolean isSerializableField(Field field) {
return !FieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(field.getType(), String.class);
return !DelegatingFieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(field.getType(), String.class);
}
private boolean isDeserializableField(Field field) {
return !FieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(String.class, field.getType());
return !DelegatingFieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(String.class, field.getType());
}
private Object serializePropertyValue(Object newVal, Class<?> fieldType) {

View File

@@ -12,8 +12,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.datastore.graph.api.*;
import org.springframework.datastore.graph.neo4j.fieldaccess.FieldAccessorFactory;
import org.springframework.datastore.graph.neo4j.finder.FinderFactory;
import org.springframework.datastore.graph.neo4j.fieldaccess.DelegatingFieldAccessorFactory;
import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields;
import org.springframework.persistence.support.EntityInstantiator;
@@ -104,7 +103,7 @@ public aspect Neo4jRelationshipBacking extends AbstractTypeAnnotatingMixinFields
if (rel == null) {
throw new InvalidDataAccessApiUsageException("Please set start node and end node before reading from other fields.");
}
String propName = FieldAccessorFactory.getNeo4jPropertyName(f);
String propName = DelegatingFieldAccessorFactory.getNeo4jPropertyName(f);
log.info("GET " + f + " <- Neo4J simple relationship property [" + propName + "]");
return deserializePropertyValue(rel.getProperty(propName, null), f.getType());
}
@@ -129,7 +128,7 @@ public aspect Neo4jRelationshipBacking extends AbstractTypeAnnotatingMixinFields
if (rel == null) {
throw new InvalidDataAccessApiUsageException("Please set start node and end node before assigning to other fields.");
}
String propName = FieldAccessorFactory.getNeo4jPropertyName(f);
String propName = DelegatingFieldAccessorFactory.getNeo4jPropertyName(f);
if (newVal==null) {
entity.getUnderlyingRelationship().removeProperty(propName);
}
@@ -164,11 +163,11 @@ public aspect Neo4jRelationshipBacking extends AbstractTypeAnnotatingMixinFields
}
private boolean isSerializableField(Field field) {
return !FieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(field.getType(), String.class);
return !DelegatingFieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(field.getType(), String.class);
}
private boolean isDeserializableField(Field field) {
return !FieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(String.class, field.getType());
return !DelegatingFieldAccessorFactory.isRelationshipField(field) && conversionService.canConvert(String.class, field.getType());
}
private Object serializePropertyValue(Object newVal, Class<?> fieldType) {