diff --git a/src/main/java/com/arangodb/springframework/core/convert/ArangoEntityReader.java b/src/main/java/com/arangodb/springframework/core/convert/ArangoEntityReader.java index 3a55bff1..b2de69b0 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/ArangoEntityReader.java +++ b/src/main/java/com/arangodb/springframework/core/convert/ArangoEntityReader.java @@ -27,7 +27,6 @@ /** * @author Mark Vollmary * @author Christian Lechner - * */ public interface ArangoEntityReader extends EntityReader { diff --git a/src/main/java/com/arangodb/springframework/core/convert/DefaultArangoConverter.java b/src/main/java/com/arangodb/springframework/core/convert/DefaultArangoConverter.java index c1ed39f4..7ff009a9 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/DefaultArangoConverter.java +++ b/src/main/java/com/arangodb/springframework/core/convert/DefaultArangoConverter.java @@ -222,8 +222,8 @@ private void readProperty( final String parentId, final PersistentPropertyAccessor accessor, final JsonNode source, - final ArangoPersistentProperty property) { - + final ArangoPersistentProperty property + ) { Object propertyValue = readPropertyValue(entity, parentId, source, property); if (propertyValue != null || !property.getType().isPrimitive()) { accessor.setProperty(property, propertyValue); @@ -234,8 +234,8 @@ private Object readPropertyValue( final ArangoPersistentEntity entity, final String parentId, final JsonNode source, - final ArangoPersistentProperty property) { - + final ArangoPersistentProperty property + ) { Optional ref = property.getRef(); if (ref.isPresent()) { return readReference(source, property, ref.get()).orElse(null); @@ -329,8 +329,8 @@ private Object readArray(final TypeInformation type, final JsonNode source) { private Optional readReference( final JsonNode source, final ArangoPersistentProperty property, - final Annotation annotation) { - + final Annotation annotation + ) { if (source.isMissingNode() || source.isNull()) { return Optional.empty(); } @@ -366,8 +366,8 @@ private Optional readRelation( final String parentId, final JsonNode source, final ArangoPersistentProperty property, - final A annotation) { - + final A annotation + ) { if (source.isNull()) { return Optional.empty(); } @@ -505,8 +505,8 @@ private DBDocumentEntity readDBDocumentEntity(final JsonNode source) { private ParameterValueProvider getParameterProvider( final ArangoPersistentEntity entity, - final JsonNode source) { - + final JsonNode source + ) { PropertyValueProvider provider = new ArangoPropertyValueProvider(entity, source); return new PersistentEntityParameterValueProvider<>(entity, provider, null); } @@ -781,7 +781,7 @@ private Optional getRefId(final Object source, final ArangoPersistentEnt .map(key -> { if (annotation != null) { return resolverFactory.getReferenceResolver(annotation) - .map(resolver -> resolver.write(source, entity, convertId(key), annotation)) + .map(resolver -> resolver.write(source, entity, convertId(key))) .orElseThrow(() -> new IllegalArgumentException("Missing reference resolver for " + annotation)); } else { return MetadataUtils.createIdFromCollectionAndKey(entity.getCollection(), convertId(key)); diff --git a/src/main/java/com/arangodb/springframework/core/convert/resolver/AbstractResolver.java b/src/main/java/com/arangodb/springframework/core/convert/resolver/AbstractResolver.java index 14da9cf4..a98372dc 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/resolver/AbstractResolver.java +++ b/src/main/java/com/arangodb/springframework/core/convert/resolver/AbstractResolver.java @@ -21,8 +21,8 @@ package com.arangodb.springframework.core.convert.resolver; import java.io.Serializable; -import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.function.Supplier; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.ProxyFactory; @@ -39,9 +39,8 @@ /** * @author Mark Vollmary * @author Christian Lechner - * */ -public abstract class AbstractResolver { +public abstract class AbstractResolver { private static final Method GET_ENTITY_METHOD; private static final Method GET_REF_ID_METHOD; @@ -66,19 +65,11 @@ protected AbstractResolver(final ConversionService conversionService) { this.objenesis = new ObjenesisStd(true); } - public interface ResolverCallback { - - Object resolve(String id, TypeInformation type, A annotation); - - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) protected Object proxy( final String id, final TypeInformation type, - final A annotation, - final ResolverCallback callback) { - final ProxyInterceptor interceptor = new ProxyInterceptor(id, type, annotation, callback, conversionService); + final Supplier callback) { + final ProxyInterceptor interceptor = new ProxyInterceptor(id, type, callback, conversionService); if (type.getType().isInterface()) { final ProxyFactory proxyFactory = new ProxyFactory(new Class[] { type.getType() }); for (final Class interf : type.getType().getInterfaces()) { @@ -102,24 +93,22 @@ private Class enhancedTypeFor(final Class type) { return enhancer.createClass(); } - static class ProxyInterceptor implements Serializable, + static class ProxyInterceptor implements Serializable, org.springframework.cglib.proxy.MethodInterceptor, org.aopalliance.intercept.MethodInterceptor { private static final long serialVersionUID = -6722757823918987065L; private final String id; final TypeInformation type; - private final A annotation; - private final ResolverCallback callback; + private final Supplier callback; private volatile boolean resolved; private Object result; private final ConversionService conversionService; - public ProxyInterceptor(final String id, final TypeInformation type, final A annotation, - final ResolverCallback callback, final ConversionService conversionService) { + public ProxyInterceptor(final String id, final TypeInformation type, + final Supplier callback, final ConversionService conversionService) { super(); this.id = id; this.type = type; - this.annotation = annotation; this.callback = callback; this.conversionService = conversionService; result = null; @@ -178,7 +167,7 @@ private Object ensureResolved() { private synchronized Object resolve() { if (!resolved) { - return convertIfNecessary(callback.resolve(id, type, annotation), type.getType()); + return convertIfNecessary(callback.get(), type.getType()); } return result; } diff --git a/src/main/java/com/arangodb/springframework/core/convert/resolver/DocumentFromResolver.java b/src/main/java/com/arangodb/springframework/core/convert/resolver/DocumentFromResolver.java index 46fa5ef1..4f32a8cc 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/resolver/DocumentFromResolver.java +++ b/src/main/java/com/arangodb/springframework/core/convert/resolver/DocumentFromResolver.java @@ -30,49 +30,50 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; /** * @author Mark Vollmary * @author Christian Lechner - * */ -public class DocumentFromResolver extends AbstractResolver implements RelationResolver { +public class DocumentFromResolver extends AbstractResolver implements RelationResolver { - private final ArangoOperations template; + private final ArangoOperations template; - public DocumentFromResolver(final ArangoOperations template) { - super(template.getConverter().getConversionService()); - this.template = template; - } + public DocumentFromResolver(final ArangoOperations template) { + super(template.getConverter().getConversionService()); + this.template = template; + } - @Override - public Object resolveOne(final String id, final TypeInformation type, Collection> traversedTypes, final From annotation) { - return annotation.lazy() ? proxy(id, type, annotation, (i, t, a) -> _resolveOne(i, t)) - : _resolveOne(id, type); - } + @Override + public Object resolveOne(final String id, final TypeInformation type, Collection> traversedTypes, + final From annotation) { + Supplier supplier = () -> _resolveOne(id, type); + return annotation.lazy() ? proxy(id, type, supplier) : supplier.get(); + } - private Object _resolveOne(final String id, final TypeInformation type) { - ArangoCursor it = _resolve(id, type.getType(), true); - return it.hasNext() ? it.next() : null; - } + private Object _resolveOne(final String id, final TypeInformation type) { + ArangoCursor it = _resolve(id, type.getType(), true); + return it.hasNext() ? it.next() : null; + } - @Override - public Object resolveMultiple(final String id, final TypeInformation type, Collection> traversedTypes, final From annotation) { - return annotation.lazy() ? proxy(id, type, annotation, (i, t, a) -> _resolveMultiple(i, t)) - : _resolveMultiple(id, type); - } + @Override + public Object resolveMultiple(final String id, final TypeInformation type, Collection> traversedTypes, + final From annotation) { + Supplier supplier = () -> _resolveMultiple(id, type); + return annotation.lazy() ? proxy(id, type, supplier) : supplier.get(); + } - private Object _resolveMultiple(final String id, final TypeInformation type) { - return _resolve(id, getNonNullComponentType(type).getType(), false).asListRemaining(); - } + private Object _resolveMultiple(final String id, final TypeInformation type) { + return _resolve(id, getNonNullComponentType(type).getType(), false).asListRemaining(); + } - private ArangoCursor _resolve(final String id, final Class type, final boolean limit) { - final String query = String.format("FOR e IN @@edge FILTER e._from == @id %s RETURN e", limit ? "LIMIT 1" : ""); - Map bindVars = new HashMap<>(); - bindVars.put("@edge", type); - bindVars.put("id", id); - return template.query(query, bindVars, new AqlQueryOptions(), - type); - } + private ArangoCursor _resolve(final String id, final Class type, final boolean limit) { + final String query = String.format("FOR e IN @@edge FILTER e._from == @id %s RETURN e", limit ? "LIMIT 1" : ""); + Map bindVars = new HashMap<>(); + bindVars.put("@edge", type); + bindVars.put("id", id); + return template.query(query, bindVars, new AqlQueryOptions(), type); + } } diff --git a/src/main/java/com/arangodb/springframework/core/convert/resolver/DocumentToResolver.java b/src/main/java/com/arangodb/springframework/core/convert/resolver/DocumentToResolver.java index c4fdc7e0..e8440a4e 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/resolver/DocumentToResolver.java +++ b/src/main/java/com/arangodb/springframework/core/convert/resolver/DocumentToResolver.java @@ -30,47 +30,50 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; /** * @author Mark Vollmary * @author Christian Lechner - * */ -public class DocumentToResolver extends AbstractResolver implements RelationResolver { +public class DocumentToResolver extends AbstractResolver implements RelationResolver { - private final ArangoOperations template; + private final ArangoOperations template; - public DocumentToResolver(final ArangoOperations template) { - super(template.getConverter().getConversionService()); - this.template = template; - } + public DocumentToResolver(final ArangoOperations template) { + super(template.getConverter().getConversionService()); + this.template = template; + } - @Override - public Object resolveOne(final String id, final TypeInformation type, Collection> traversedTypes, final To annotation) { - return annotation.lazy() ? proxy(id, type, annotation, (i, t, a) -> _resolveOne(i, t)) : _resolveOne(id, type); - } + @Override + public Object resolveOne(final String id, final TypeInformation type, Collection> traversedTypes, + final To annotation) { + Supplier supplier = () -> _resolveOne(id, type); + return annotation.lazy() ? proxy(id, type, supplier) : supplier.get(); + } - private Object _resolveOne(final String id, final TypeInformation type) { - ArangoCursor it = _resolve(id, type.getType(), true); - return it.hasNext() ? it.next() : null; - } + private Object _resolveOne(final String id, final TypeInformation type) { + ArangoCursor it = _resolve(id, type.getType(), true); + return it.hasNext() ? it.next() : null; + } - @Override - public Object resolveMultiple(final String id, final TypeInformation type, Collection> traversedTypes, final To annotation) { - return annotation.lazy() ? proxy(id, type, annotation, (i, t, a) -> _resolveMultiple(i, t)) - : _resolveMultiple(id, type); - } + @Override + public Object resolveMultiple(final String id, final TypeInformation type, Collection> traversedTypes, + final To annotation) { + Supplier supplier = () -> _resolveMultiple(id, type); + return annotation.lazy() ? proxy(id, type, supplier) : supplier.get(); + } - private Object _resolveMultiple(final String id, final TypeInformation type) { - return _resolve(id, getNonNullComponentType(type).getType(), false).asListRemaining(); - } + private Object _resolveMultiple(final String id, final TypeInformation type) { + return _resolve(id, getNonNullComponentType(type).getType(), false).asListRemaining(); + } - private ArangoCursor _resolve(final String id, final Class type, final boolean limit) { - final String query = String.format("FOR e IN @@edge FILTER e._to == @id %s RETURN e", limit ? "LIMIT 1" : ""); - Map bindVars = new HashMap<>(); - bindVars.put("@edge", type); - bindVars.put("id", id); - return template.query(query, bindVars, new AqlQueryOptions(), type); - } + private ArangoCursor _resolve(final String id, final Class type, final boolean limit) { + final String query = String.format("FOR e IN @@edge FILTER e._to == @id %s RETURN e", limit ? "LIMIT 1" : ""); + Map bindVars = new HashMap<>(); + bindVars.put("@edge", type); + bindVars.put("id", id); + return template.query(query, bindVars, new AqlQueryOptions(), type); + } } diff --git a/src/main/java/com/arangodb/springframework/core/convert/resolver/EdgeFromResolver.java b/src/main/java/com/arangodb/springframework/core/convert/resolver/EdgeFromResolver.java index 40ee75c8..a1dcc083 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/resolver/EdgeFromResolver.java +++ b/src/main/java/com/arangodb/springframework/core/convert/resolver/EdgeFromResolver.java @@ -26,12 +26,12 @@ import com.arangodb.springframework.core.ArangoOperations; import java.util.Collection; +import java.util.function.Supplier; /** * @author Mark Vollmary - * */ -public class EdgeFromResolver extends AbstractResolver implements RelationResolver { +public class EdgeFromResolver extends AbstractResolver implements RelationResolver { private final ArangoOperations template; @@ -41,8 +41,10 @@ public EdgeFromResolver(final ArangoOperations template) { } @Override - public Object resolveOne(final String id, final TypeInformation type, Collection> traversedTypes, final From annotation) { - return annotation.lazy() ? proxy(id, type, annotation, (i, t, a) -> _resolveOne(i, t)) : _resolveOne(id, type); + public Object resolveOne(final String id, final TypeInformation type, Collection> traversedTypes, + final From annotation) { + Supplier supplier = () -> _resolveOne(id, type); + return annotation.lazy() ? proxy(id, type, supplier) : supplier.get(); } private Object _resolveOne(final String id, final TypeInformation type) { @@ -51,7 +53,8 @@ private Object _resolveOne(final String id, final TypeInformation type) { } @Override - public Object resolveMultiple(final String id, final TypeInformation type, Collection> traversedTypes, final From annotation) { + public Object resolveMultiple(final String id, final TypeInformation type, Collection> traversedTypes, + final From annotation) { throw new UnsupportedOperationException("Edges with multiple 'from' values are not supported."); } diff --git a/src/main/java/com/arangodb/springframework/core/convert/resolver/EdgeToResolver.java b/src/main/java/com/arangodb/springframework/core/convert/resolver/EdgeToResolver.java index d16250be..69e92369 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/resolver/EdgeToResolver.java +++ b/src/main/java/com/arangodb/springframework/core/convert/resolver/EdgeToResolver.java @@ -26,12 +26,12 @@ import com.arangodb.springframework.core.ArangoOperations; import java.util.Collection; +import java.util.function.Supplier; /** * @author Mark Vollmary - * */ -public class EdgeToResolver extends AbstractResolver implements RelationResolver { +public class EdgeToResolver extends AbstractResolver implements RelationResolver { private final ArangoOperations template; @@ -41,8 +41,10 @@ public EdgeToResolver(final ArangoOperations template) { } @Override - public Object resolveOne(final String id, final TypeInformation type, Collection> traversedTypes, final To annotation) { - return annotation.lazy() ? proxy(id, type, annotation, (i, t, a) -> _resolveOne(i, t)) : _resolveOne(id, type); + public Object resolveOne(final String id, final TypeInformation type, Collection> traversedTypes, + final To annotation) { + Supplier supplier = () -> _resolveOne(id, type); + return annotation.lazy() ? proxy(id, type, supplier) : supplier.get(); } private Object _resolveOne(final String id, final TypeInformation type) { diff --git a/src/main/java/com/arangodb/springframework/core/convert/resolver/RefResolver.java b/src/main/java/com/arangodb/springframework/core/convert/resolver/RefResolver.java index 39d46895..6dfc8058 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/resolver/RefResolver.java +++ b/src/main/java/com/arangodb/springframework/core/convert/resolver/RefResolver.java @@ -21,6 +21,7 @@ package com.arangodb.springframework.core.convert.resolver; import java.util.Collection; +import java.util.function.Supplier; import java.util.stream.Collectors; import com.arangodb.springframework.core.mapping.ArangoPersistentEntity; @@ -33,43 +34,37 @@ /** * @author Mark Vollmary * @author Christian Lechner - * */ -public class RefResolver extends AbstractResolver - implements ReferenceResolver, AbstractResolver.ResolverCallback { - - private final ArangoOperations template; - - public RefResolver(final ArangoOperations template) { - super(template.getConverter().getConversionService()); - this.template = template; - } +public class RefResolver extends AbstractResolver implements ReferenceResolver { - @Override - public Object resolveOne(final String id, final TypeInformation type, final Ref annotation) { - return annotation.lazy() ? proxy(id, type, annotation, this) : _resolve(id, type); - } + private final ArangoOperations template; - @Override - public Object resolveMultiple(final Collection ids, final TypeInformation type, final Ref annotation) { - return ids.stream().map(id -> resolveOne(id, getNonNullComponentType(type), annotation)) - .collect(Collectors.toList()); - } + public RefResolver(final ArangoOperations template) { + super(template.getConverter().getConversionService()); + this.template = template; + } - @Override - public Object resolve(final String id, final TypeInformation type, final Ref annotation) { - return _resolve(id, type); - } + @Override + public Object resolveOne(final String id, final TypeInformation type, final Ref annotation) { + Supplier supplier = () -> _resolve(id, type); + return annotation.lazy() ? proxy(id, type, supplier) : supplier.get(); + } - public Object _resolve(final String id, final TypeInformation type) { - return template.find(id, type.getType()) - .orElseThrow(() -> cannotResolveException(id, type)); - } + @Override + public Object resolveMultiple(final Collection ids, final TypeInformation type, final Ref annotation) { + return ids.stream() + .map(id -> resolveOne(id, getNonNullComponentType(type), annotation)) + .collect(Collectors.toList()); + } - @Override - public String write(final Object source, final ArangoPersistentEntity entity, final Object id, final Ref annotation) { - return MetadataUtils.createIdFromCollectionAndKey(entity.getCollection(), String.valueOf(id)); - } + private Object _resolve(final String id, final TypeInformation type) { + return template.find(id, type.getType()) + .orElseThrow(() -> cannotResolveException(id, type)); + } + @Override + public String write(final Object source, final ArangoPersistentEntity entity, final Object id) { + return MetadataUtils.createIdFromCollectionAndKey(entity.getCollection(), String.valueOf(id)); + } } diff --git a/src/main/java/com/arangodb/springframework/core/convert/resolver/ReferenceResolver.java b/src/main/java/com/arangodb/springframework/core/convert/resolver/ReferenceResolver.java index 667aadb5..17c62411 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/resolver/ReferenceResolver.java +++ b/src/main/java/com/arangodb/springframework/core/convert/resolver/ReferenceResolver.java @@ -23,20 +23,14 @@ import java.lang.annotation.Annotation; import java.util.Collection; -import com.arangodb.springframework.annotation.Ref; import com.arangodb.springframework.core.mapping.ArangoPersistentEntity; import org.springframework.data.util.TypeInformation; /** * @author Mark Vollmary - * */ public interface ReferenceResolver { - Object resolveOne(String id, TypeInformation type, A annotation); - Object resolveMultiple(Collection ids, TypeInformation type, A annotation); - - public String write(Object source, ArangoPersistentEntity entity, Object id, Ref annotation); - + String write(Object source, ArangoPersistentEntity entity, Object id); } diff --git a/src/main/java/com/arangodb/springframework/core/convert/resolver/RelationResolver.java b/src/main/java/com/arangodb/springframework/core/convert/resolver/RelationResolver.java index b5af2b69..6cd62c18 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/resolver/RelationResolver.java +++ b/src/main/java/com/arangodb/springframework/core/convert/resolver/RelationResolver.java @@ -27,12 +27,9 @@ /** * @author Mark Vollmary - * */ public interface RelationResolver { - Object resolveOne(String id, TypeInformation type, Collection> traversedTypes, A annotation); - Object resolveMultiple(String id, TypeInformation type, Collection> traversedTypes, A annotation); } diff --git a/src/main/java/com/arangodb/springframework/core/convert/resolver/RelationsResolver.java b/src/main/java/com/arangodb/springframework/core/convert/resolver/RelationsResolver.java index e0f6aa07..7a69c892 100644 --- a/src/main/java/com/arangodb/springframework/core/convert/resolver/RelationsResolver.java +++ b/src/main/java/com/arangodb/springframework/core/convert/resolver/RelationsResolver.java @@ -21,6 +21,7 @@ package com.arangodb.springframework.core.convert.resolver; import java.util.*; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.springframework.data.util.TypeInformation; @@ -32,79 +33,82 @@ /** * @author Mark Vollmary * @author Christian Lechner - * */ -public class RelationsResolver extends AbstractResolver implements RelationResolver { - - private final ArangoOperations template; - - public RelationsResolver(final ArangoOperations template) { - super(template.getConverter().getConversionService()); - this.template = template; - } - - @Override - public Object resolveOne(final String id, final TypeInformation type, final Collection> traversedTypes, final Relations annotation) { - return annotation.lazy() ? proxy(id, type, annotation, (i, t, a) -> _resolveOne(i, t, traversedTypes, a)) - : _resolveOne(id, type, traversedTypes, annotation); - } - - @Override - public Object resolveMultiple(final String id, final TypeInformation type, final Collection> traversedTypes, final Relations annotation) { - return annotation.lazy() ? proxy(id, type, annotation, (i, t, a) -> _resolveMultiple(i, t, traversedTypes, a)) - : _resolveMultiple(id, type, traversedTypes, annotation); - } - - private Object _resolveOne(final String id, final TypeInformation type, final Collection> traversedTypes, final Relations annotation) { - Collection> rawTypes = new ArrayList<>(); - for (TypeInformation it : traversedTypes) { - rawTypes.add(it.getType()); - } - ArangoCursor it = _resolve(id, type.getType(), rawTypes, annotation, true); - return it.hasNext() ? it.next() : null; - } - - private Object _resolveMultiple(final String id, final TypeInformation type, final Collection> traversedTypes, final Relations annotation) { - Collection> rawTypes = new ArrayList<>(); - for (TypeInformation it : traversedTypes) { - rawTypes.add(it.getType()); - } - return _resolve(id, getNonNullComponentType(type).getType(), rawTypes, annotation, false).asListRemaining(); - } - - private ArangoCursor _resolve( - final String id, - final Class type, - final Collection> traversedTypes, - final Relations annotation, - final boolean limit) { - - final String edges = Arrays.stream(annotation.edges()).map(e -> template.collection(e).name()) - .collect(Collectors.joining(",")); - - List> allTraversedTypes = new ArrayList<>(); - allTraversedTypes.add(type); - allTraversedTypes.addAll(traversedTypes); - - Map bindVars = new HashMap<>(); - bindVars.put("start", id); - StringBuilder withClause = new StringBuilder("WITH "); - for (int i = 0; i < allTraversedTypes.size(); i++) { - bindVars.put("@with" + i, allTraversedTypes.get(i)); - if (i > 0) withClause.append(", "); - withClause.append("@@with").append(i); - } - - final String query = String.format( - "%s FOR v IN %d .. %d %s @start %s OPTIONS {bfs: true, uniqueVertices: \"global\"} %s RETURN v", // - withClause, // - Math.max(1, annotation.minDepth()), // - Math.max(1, annotation.maxDepth()), // - annotation.direction(), // - edges, // - limit ? "LIMIT 1" : ""); - - return template.query(query, bindVars, type); - } +public class RelationsResolver extends AbstractResolver implements RelationResolver { + + private final ArangoOperations template; + + public RelationsResolver(final ArangoOperations template) { + super(template.getConverter().getConversionService()); + this.template = template; + } + + @Override + public Object resolveOne(final String id, final TypeInformation type, final Collection> traversedTypes, + final Relations annotation) { + Supplier supplier = () -> _resolveOne(id, type, traversedTypes, annotation); + return annotation.lazy() ? proxy(id, type, supplier) : supplier.get(); + } + + @Override + public Object resolveMultiple(final String id, final TypeInformation type, final Collection> traversedTypes, + final Relations annotation) { + Supplier supplier = () -> _resolveMultiple(id, type, traversedTypes, annotation); + return annotation.lazy() ? proxy(id, type, supplier) : supplier.get(); + } + + private Object _resolveOne(final String id, final TypeInformation type, final Collection> traversedTypes, + final Relations annotation) { + Collection> rawTypes = new ArrayList<>(); + for (TypeInformation it : traversedTypes) { + rawTypes.add(it.getType()); + } + ArangoCursor it = _resolve(id, type.getType(), rawTypes, annotation, true); + return it.hasNext() ? it.next() : null; + } + + private Object _resolveMultiple(final String id, final TypeInformation type, final Collection> traversedTypes, + final Relations annotation) { + Collection> rawTypes = new ArrayList<>(); + for (TypeInformation it : traversedTypes) { + rawTypes.add(it.getType()); + } + return _resolve(id, getNonNullComponentType(type).getType(), rawTypes, annotation, false).asListRemaining(); + } + + private ArangoCursor _resolve( + final String id, + final Class type, + final Collection> traversedTypes, + final Relations annotation, + final boolean limit) { + + final String edges = Arrays.stream(annotation.edges()).map(e -> template.collection(e).name()) + .collect(Collectors.joining(",")); + + List> allTraversedTypes = new ArrayList<>(); + allTraversedTypes.add(type); + allTraversedTypes.addAll(traversedTypes); + + Map bindVars = new HashMap<>(); + bindVars.put("start", id); + StringBuilder withClause = new StringBuilder("WITH "); + for (int i = 0; i < allTraversedTypes.size(); i++) { + bindVars.put("@with" + i, allTraversedTypes.get(i)); + if (i > 0) withClause.append(", "); + withClause.append("@@with").append(i); + } + + final String query = String.format( + "%s FOR v IN %d .. %d %s @start %s OPTIONS {bfs: true, uniqueVertices: \"global\"} %s RETURN v", // + withClause, // + Math.max(1, annotation.minDepth()), // + Math.max(1, annotation.maxDepth()), // + annotation.direction(), // + edges, // + limit ? "LIMIT 1" : ""); + + return template.query(query, bindVars, type); + } } diff --git a/src/main/java/com/arangodb/springframework/core/mapping/ArangoSimpleTypes.java b/src/main/java/com/arangodb/springframework/core/mapping/ArangoSimpleTypes.java index b02c2379..1f6dcd07 100644 --- a/src/main/java/com/arangodb/springframework/core/mapping/ArangoSimpleTypes.java +++ b/src/main/java/com/arangodb/springframework/core/mapping/ArangoSimpleTypes.java @@ -51,8 +51,9 @@ public final class ArangoSimpleTypes { static { final Set> simpleTypes = new HashSet<>(); - // com.arangodb.* simpleTypes.add(JsonNode.class); + + // com.arangodb.* simpleTypes.add(DBDocumentEntity.class); // java.math.* diff --git a/src/main/java/com/arangodb/springframework/repository/ArangoExampleConverter.java b/src/main/java/com/arangodb/springframework/repository/ArangoExampleConverter.java index 380b464a..db8f2555 100644 --- a/src/main/java/com/arangodb/springframework/repository/ArangoExampleConverter.java +++ b/src/main/java/com/arangodb/springframework/repository/ArangoExampleConverter.java @@ -130,7 +130,7 @@ private void traversePropertyTree(final Example example, final StringBuilder if (property.getRef().isPresent()) { final Optional> resolver = resolverFactory .getReferenceResolver(property.getRef().get()); - refIdValue = resolver.get().write(value, persistentEntity, idValue, property.getRef().get()); + refIdValue = resolver.get().write(value, persistentEntity, idValue); } else { refIdValue = String.format("%s/%s", persistentEntity.getCollection(), idValue); } diff --git a/src/main/java/com/arangodb/springframework/repository/query/AbstractArangoQuery.java b/src/main/java/com/arangodb/springframework/repository/query/AbstractArangoQuery.java index 2385326a..54c1f6af 100644 --- a/src/main/java/com/arangodb/springframework/repository/query/AbstractArangoQuery.java +++ b/src/main/java/com/arangodb/springframework/repository/query/AbstractArangoQuery.java @@ -201,8 +201,7 @@ private Object convertResult(final ArangoCursor result, final ArangoParameter } return (Integer) result.next() > 0; } - final ArangoResultConverter resultConverter = new ArangoResultConverter(accessor, result, operations, - domainClass); + final ArangoResultConverter resultConverter = new ArangoResultConverter<>(accessor, result, operations, domainClass); return resultConverter.convertResult(method.getReturnType().getType()); } diff --git a/src/main/java/com/arangodb/springframework/repository/query/ArangoResultConverter.java b/src/main/java/com/arangodb/springframework/repository/query/ArangoResultConverter.java index 6e2e6ae0..fda4664c 100644 --- a/src/main/java/com/arangodb/springframework/repository/query/ArangoResultConverter.java +++ b/src/main/java/com/arangodb/springframework/repository/query/ArangoResultConverter.java @@ -20,12 +20,9 @@ package com.arangodb.springframework.repository.query; -import java.lang.reflect.Method; import java.util.Collection; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.Spliterators; @@ -55,7 +52,7 @@ * @author Mark Vollmary * @author Christian Lechner */ -public class ArangoResultConverter { +public class ArangoResultConverter { private final static String MISSING_FULL_COUNT = "Query result does not contain the full result count! " + "The most likely cause is a forgotten LIMIT clause in the query."; @@ -63,43 +60,17 @@ public class ArangoResultConverter { private final ArangoParameterAccessor accessor; private final ArangoCursor result; private final ArangoOperations operations; - private final Class domainClass; + private final Class domainClass; - private static Map TYPE_MAP = new HashMap<>(); - - /** - * Build static map of all supported return types and the method used to convert them - */ - static { - try { - TYPE_MAP.put(List.class, ArangoResultConverter.class.getMethod("convertList")); - TYPE_MAP.put(Iterable.class, ArangoResultConverter.class.getMethod("convertList")); - TYPE_MAP.put(Collection.class, ArangoResultConverter.class.getMethod("convertList")); - TYPE_MAP.put(Page.class, ArangoResultConverter.class.getMethod("convertPage")); - TYPE_MAP.put(Slice.class, ArangoResultConverter.class.getMethod("convertPage")); - TYPE_MAP.put(Set.class, ArangoResultConverter.class.getMethod("convertSet")); - TYPE_MAP.put(ArangoCursor.class, ArangoResultConverter.class.getMethod("convertArangoCursor")); - TYPE_MAP.put(GeoResult.class, ArangoResultConverter.class.getMethod("convertGeoResult")); - TYPE_MAP.put(GeoResults.class, ArangoResultConverter.class.getMethod("convertGeoResults")); - TYPE_MAP.put(GeoPage.class, ArangoResultConverter.class.getMethod("convertGeoPage")); - TYPE_MAP.put(Optional.class, ArangoResultConverter.class.getMethod("convertOptional")); - TYPE_MAP.put("array", ArangoResultConverter.class.getMethod("convertArray")); - } catch (final NoSuchMethodException e) { - e.printStackTrace(); - } - } /** * @param accessor - * @param result - * the query result returned by the driver - * @param operations - * instance of arangoTemplate - * @param domainClass - * class type of documents + * @param result the query result returned by the driver + * @param operations instance of arangoTemplate + * @param domainClass class type of documents */ public ArangoResultConverter(final ArangoParameterAccessor accessor, final ArangoCursor result, - final ArangoOperations operations, final Class domainClass) { + final ArangoOperations operations, final Class domainClass) { this.accessor = accessor; this.result = result; this.operations = operations; @@ -114,23 +85,40 @@ public ArangoResultConverter(final ArangoParameterAccessor accessor, final Arang */ public Object convertResult(final Class type) { try { + return convert(type); + } catch (final Exception e) { + throw new MappingException(String.format("Can't convert result to type %s!", type.getName()), e); + } + } + + private Object convert(Class type) { if (type.isArray()) { - return TYPE_MAP.get("array").invoke(this); - } - if (!TYPE_MAP.containsKey(type)) { + return convertArray(); + } else if (List.class.equals(type) || Iterable.class.equals(type) || Collection.class.equals(type)) { + return convertList(); + } else if (Page.class.equals(type) || Slice.class.equals(type)) { + return convertPage(); + } else if (Set.class.equals(type)) { + return convertSet(); + } else if (ArangoCursor.class.equals(type)) { + return convertArangoCursor(); + } else if (GeoResult.class.equals(type)) { + return convertGeoResult(); + } else if (GeoResults.class.equals(type)) { + return convertGeoResults(); + } else if (GeoPage.class.equals(type)) { + return convertGeoPage(); + } else if (Optional.class.equals(type)) { + return convertOptional(); + } else { return getNext(result); } - return TYPE_MAP.get(type).invoke(this); - } catch (final Exception e) { - throw new MappingException(String.format("Can't convert result to type %s!", type.getName()), e); - } } /** * Creates a Set return type from the given cursor * - * @param cursor - * query result from driver + * @param cursor query result from driver * @return Set containing the results */ private Set buildSet(final ArangoCursor cursor) { @@ -140,73 +128,41 @@ private Set buildSet(final ArangoCursor cursor) { /** * Build a GeoResult from the given ArangoCursor * - * @param cursor - * query result from driver + * @param cursor query result from driver * @return GeoResult object */ - private GeoResult buildGeoResult(final ArangoCursor cursor) { - GeoResult geoResult = null; - while (cursor.hasNext() && geoResult == null) { - final Object object = cursor.next(); - if (!(object instanceof JsonNode)) { - continue; - } - - final JsonNode slice = (JsonNode) object; - final JsonNode distSlice = slice.get("_distance"); - final Double distanceInMeters = distSlice.isDouble() ? distSlice.doubleValue() : null; - if (distanceInMeters == null) { - continue; - } - - final Object entity = operations.getConverter().read(domainClass, slice); - final Distance distance = new Distance(distanceInMeters / 1000, Metrics.KILOMETERS); - geoResult = new GeoResult<>(entity, distance); - } - return geoResult; + private GeoResult buildGeoResult(final ArangoCursor cursor) { + return buildGeoResult(cursor.next()); } /** * Construct a GeoResult from the given object * - * @param object - * object representing one document in the result + * @param slice object representing one document in the result * @return GeoResult object */ - private GeoResult buildGeoResult(final Object object) { - if (object == null || !(object instanceof JsonNode)) { - return null; - } - - final JsonNode slice = (JsonNode) object; - final JsonNode distSlice = slice.get("_distance"); - final Double distanceInMeters = distSlice.isDouble() ? distSlice.doubleValue() : null; - if (distanceInMeters == null) { - return null; - } - - final Object entity = operations.getConverter().read(domainClass, slice); - final Distance distance = new Distance(distanceInMeters / 1000, Metrics.KILOMETERS); + private GeoResult buildGeoResult(final JsonNode slice) { + JsonNode distSlice = slice.get("_distance"); + Double distanceInMeters = distSlice.isDouble() ? distSlice.doubleValue() : null; + T entity = operations.getConverter().read(domainClass, slice); + // FIXME: Unboxing of 'distanceInMeters' may produce 'NullPointerException' + Distance distance = new Distance(distanceInMeters / 1000, Metrics.KILOMETERS); return new GeoResult<>(entity, distance); } /** * Build a GeoResults object with the ArangoCursor returned by query execution * - * @param cursor - * ArangoCursor containing query results + * @param cursor ArangoCursor containing query results * @return GeoResults object with all results */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private GeoResults buildGeoResults(final ArangoCursor cursor) { - final List> list = new LinkedList<>(); - cursor.forEachRemaining(o -> { - final GeoResult geoResult = buildGeoResult(o); - if (geoResult != null) { - list.add(geoResult); - } - }); - return new GeoResults(list); + private GeoResults buildGeoResults(final ArangoCursor cursor) { + final List> list = new LinkedList<>(); + cursor.forEachRemaining(o -> list.add(buildGeoResult(o))); + // FIXME: DE-803 + // convert geoResults to Metrics.NEUTRAL before + // invoking GeoResults.GeoResults(java.util.List>) + return new GeoResults<>(list); } public Optional convertOptional() { @@ -230,17 +186,20 @@ public ArangoCursor convertArangoCursor() { return result; } - public GeoResult convertGeoResult() { - return buildGeoResult(result); + @SuppressWarnings("unchecked") + public GeoResult convertGeoResult() { + return buildGeoResult((ArangoCursor) result); } - public GeoResults convertGeoResults() { - return buildGeoResults(result); + @SuppressWarnings("unchecked") + public GeoResults convertGeoResults() { + return buildGeoResults((ArangoCursor) result); } - public GeoPage convertGeoPage() { + @SuppressWarnings("unchecked") + public GeoPage convertGeoPage() { Assert.notNull(result.getStats().getFullCount(), MISSING_FULL_COUNT); - return new GeoPage<>(buildGeoResults(result), accessor.getPageable(), ((Number) result.getStats().getFullCount()).longValue()); + return new GeoPage<>(buildGeoResults((ArangoCursor) result), accessor.getPageable(), ((Number) result.getStats().getFullCount()).longValue()); } public Object convertArray() {