diff --git a/pom.xml b/pom.xml index 2917aeb0..5c1acd37 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-keyvalue - 0.1.0.BUILD-SNAPSHOT + 0.1.0.DATAKV-101-SNAPSHOT Spring Data KeyValue diff --git a/src/main/java/org/springframework/data/keyvalue/core/IterableConverter.java b/src/main/java/org/springframework/data/keyvalue/core/IterableConverter.java new file mode 100644 index 00000000..8cc093e6 --- /dev/null +++ b/src/main/java/org/springframework/data/keyvalue/core/IterableConverter.java @@ -0,0 +1,59 @@ +/* + * Copyright 2015 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.keyvalue.core; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Converter capable of transforming a given {@link Iterable} into a collection type. + * + * @author Christoph Strobl + * @param + */ +public final class IterableConverter { + + private IterableConverter() {} + + /** + * Converts a given {@link Iterable} into a {@link List} + * + * @param source + * @return {@link Collections#emptyList()} when source is {@literal null}. + */ + public static List toList(Iterable source) { + + if (source == null) { + return Collections.emptyList(); + } + + if (source instanceof List) { + return (List) source; + } + + if (source instanceof Collection) { + return new ArrayList((Collection) source); + } + + List result = new ArrayList(); + for (T value : source) { + result.add(value); + } + return result; + } +} diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java index 70ccbaba..14a0477a 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java @@ -71,7 +71,7 @@ public interface KeyValueAdapter extends DisposableBean { * @param keyspace must not be {@literal null}. * @return empty {@link Collection} if nothing found. */ - Collection getAllOf(Serializable keyspace); + Iterable getAllOf(Serializable keyspace); /** * Returns a {@link KeyValueIterator} that iterates over all entries. @@ -100,7 +100,14 @@ public interface KeyValueAdapter extends DisposableBean { * @param keyspace must not be {@literal null}. * @return empty {@link Collection} if no match found. */ - Collection find(KeyValueQuery query, Serializable keyspace); + Iterable find(KeyValueQuery query, Serializable keyspace); + + /** + * Count number of objects within {@literal keyspace}. + * + * @param keyspace must not be {@literal null}. + */ + long count(Serializable keyspace); /** * Count all matching objects within {@literal keyspace}. diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueOperations.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueOperations.java index 3851c812..3e17f714 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueOperations.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueOperations.java @@ -16,7 +16,6 @@ package org.springframework.data.keyvalue.core; import java.io.Serializable; -import java.util.List; import org.springframework.beans.factory.DisposableBean; import org.springframework.data.domain.Sort; @@ -52,9 +51,9 @@ public interface KeyValueOperations extends DisposableBean { * assigned to requested type. * * @param type must not be {@literal null}. - * @return empty collection if no elements found. + * @return empty iterable if no elements found. */ - List findAll(Class type); + Iterable findAll(Class type); /** * Get all elements ordered by sort. Respects {@link KeySpace} if present and therefore returns all elements that can @@ -64,7 +63,7 @@ public interface KeyValueOperations extends DisposableBean { * @param type must not be {@literal null}. * @return */ - List findAll(Sort sort, Class type); + Iterable findAll(Sort sort, Class type); /** * Get element of given type with given id. Respects {@link KeySpace} if present and therefore returns all elements @@ -90,9 +89,9 @@ public interface KeyValueOperations extends DisposableBean { * * @param query must not be {@literal null}. * @param type must not be {@literal null}. - * @return empty collection if no match found. + * @return empty iterable if no match found. */ - List find(KeyValueQuery query, Class type); + Iterable find(KeyValueQuery query, Class type); /** * Get all elements in given range. Respects {@link KeySpace} if present and therefore returns all elements that can @@ -103,7 +102,7 @@ public interface KeyValueOperations extends DisposableBean { * @param type must not be {@literal null}. * @return */ - List findInRange(int offset, int rows, Class type); + Iterable findInRange(int offset, int rows, Class type); /** * Get all elements in given range ordered by sort. Respects {@link KeySpace} if present and therefore returns all @@ -115,7 +114,7 @@ public interface KeyValueOperations extends DisposableBean { * @param type * @return */ - List findInRange(int offset, int rows, Sort sort, Class type); + Iterable findInRange(int offset, int rows, Sort sort, Class type); /** * @param objectToUpdate must not be {@literal null}. diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java index 6d677226..72180539 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java @@ -19,7 +19,6 @@ import java.io.Serializable; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -203,14 +202,14 @@ public List findAll(final Class type) { @Override public List doInKeyValue(KeyValueAdapter adapter) { - Collection x = adapter.getAllOf(resolveKeySpace(type)); + Iterable values = adapter.getAllOf(resolveKeySpace(type)); if (getKeySpace(type) == null) { - return new ArrayList((Collection) x); + return new ArrayList(IterableConverter.toList((Iterable) values)); } ArrayList filtered = new ArrayList(); - for (Object candidate : x) { + for (Object candidate : values) { if (typeCheck(type, candidate)) { filtered.add((T) candidate); } @@ -332,7 +331,7 @@ public T doInKeyValue(KeyValueAdapter adapter) { public long count(Class type) { Assert.notNull(type, "Type for count must not be null!"); - return findAll(type).size(); + return adapter.count(resolveKeySpace(type)); } /* @@ -364,10 +363,10 @@ public List find(final KeyValueQuery query, final Class type) { @Override public List doInKeyValue(KeyValueAdapter adapter) { - Collection result = adapter.find(query, resolveKeySpace(type)); + Iterable result = adapter.find(query, resolveKeySpace(type)); if (getKeySpace(type) == null) { - return new ArrayList((Collection) result); + return new ArrayList(IterableConverter.toList((Iterable) result)); } List filtered = new ArrayList(); @@ -517,7 +516,7 @@ private RuntimeException resolveExceptionIfPossible(RuntimeException e) { } private void potentiallyPublishEvent(KeyValueEvent event) { - + if (eventPublisher == null) { return; } diff --git a/src/main/java/org/springframework/data/keyvalue/core/SpelQueryEngine.java b/src/main/java/org/springframework/data/keyvalue/core/SpelQueryEngine.java index 6b7977a3..e472dc37 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/SpelQueryEngine.java +++ b/src/main/java/org/springframework/data/keyvalue/core/SpelQueryEngine.java @@ -65,10 +65,10 @@ public long count(SpelExpression criteria, Serializable keyspace) { } @SuppressWarnings({ "unchecked", "rawtypes" }) - private List sortAndFilterMatchingRange(Collection source, SpelExpression criteria, Comparator sort, - int offset, int rows) { + private List sortAndFilterMatchingRange(Iterable source, SpelExpression criteria, Comparator sort, int offset, + int rows) { - List tmp = new ArrayList(source); + List tmp = IterableConverter.toList(source); if (sort != null) { Collections.sort(tmp, sort); } diff --git a/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java b/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java index 764aceb2..efa7b279 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/query/KeyValuePartTreeQuery.java @@ -16,11 +16,11 @@ package org.springframework.data.keyvalue.repository.query; import java.lang.reflect.Constructor; -import java.util.List; import org.springframework.beans.BeanUtils; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.keyvalue.core.IterableConverter; import org.springframework.data.keyvalue.core.KeyValueOperations; import org.springframework.data.keyvalue.core.query.KeyValueQuery; import org.springframework.data.repository.query.EvaluationContextProvider; @@ -33,7 +33,6 @@ import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.util.ClassUtils; -import org.springframework.util.CollectionUtils; /** * {@link RepositoryQuery} implementation deriving queries from {@link PartTree} using a predefined @@ -77,12 +76,12 @@ public Object execute(Object[] parameters) { query.setOffset(page.getOffset()); query.setRows(page.getPageSize()); - List result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType()); + Iterable result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType()); long count = queryMethod.isSliceQuery() ? 0 : keyValueOperations.count(query, queryMethod.getEntityInformation() .getJavaType()); - return new PageImpl(result, page, count); + return new PageImpl(IterableConverter.toList(result), page, count); } else if (queryMethod.isCollectionQuery()) { @@ -90,8 +89,8 @@ public Object execute(Object[] parameters) { } else if (queryMethod.isQueryForEntity()) { - List result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType()); - return CollectionUtils.isEmpty(result) ? null : result.get(0); + Iterable result = this.keyValueOperations.find(query, queryMethod.getEntityInformation().getJavaType()); + return result.iterator().hasNext() ? result.iterator().next() : null; } diff --git a/src/main/java/org/springframework/data/keyvalue/repository/support/SimpleKeyValueRepository.java b/src/main/java/org/springframework/data/keyvalue/repository/support/SimpleKeyValueRepository.java index 5ab9d897..91ff53a3 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/support/SimpleKeyValueRepository.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/support/SimpleKeyValueRepository.java @@ -23,6 +23,7 @@ import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.keyvalue.core.IterableConverter; import org.springframework.data.keyvalue.core.KeyValueOperations; import org.springframework.data.keyvalue.repository.KeyValueRepository; import org.springframework.data.repository.core.EntityInformation; @@ -76,12 +77,11 @@ public Page findAll(Pageable pageable) { return new PageImpl(result, null, result.size()); } - List content = null; - - content = operations.findInRange(pageable.getOffset(), pageable.getPageSize(), pageable.getSort(), + Iterable content = operations.findInRange(pageable.getOffset(), pageable.getPageSize(), pageable.getSort(), entityInformation.getJavaType()); - return new PageImpl(content, pageable, this.operations.count(entityInformation.getJavaType())); + return new PageImpl(IterableConverter.toList(content), pageable, this.operations.count(entityInformation + .getJavaType())); } /* @@ -139,7 +139,7 @@ public boolean exists(ID id) { */ @Override public List findAll() { - return operations.findAll(entityInformation.getJavaType()); + return IterableConverter.toList(operations.findAll(entityInformation.getJavaType())); } /* diff --git a/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java b/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java index 3e9695c5..4c0acc85 100644 --- a/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2015 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. @@ -104,6 +104,15 @@ public boolean contains(Serializable id, Serializable keyspace) { return get(id, keyspace) != null; } + + /* (non-Javadoc) + * @see org.springframework.data.keyvalue.core.KeyValueAdapter#count(java.io.Serializable) + */ + @Override + public long count(Serializable keyspace) { + return getKeySpaceMap(keyspace).size(); + } + /* * (non-Javadoc) * @see org.springframework.data.keyvalue.core.KeyValueAdapter#get(java.io.Serializable, java.io.Serializable) diff --git a/src/test/java/org/springframework/data/keyvalue/core/IterableConverterUnitTests.java b/src/test/java/org/springframework/data/keyvalue/core/IterableConverterUnitTests.java new file mode 100644 index 00000000..382f98ba --- /dev/null +++ b/src/test/java/org/springframework/data/keyvalue/core/IterableConverterUnitTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2015 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.keyvalue.core; + +import static org.hamcrest.collection.IsEmptyCollection.*; +import static org.hamcrest.collection.IsIterableContainingInOrder.*; +import static org.hamcrest.core.IsInstanceOf.*; +import static org.hamcrest.core.IsNull.*; +import static org.hamcrest.core.IsSame.*; +import static org.junit.Assert.*; +import static org.springframework.data.keyvalue.core.IterableConverter.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Test; + +/** + * @author Christoph Strobl + */ +public class IterableConverterUnitTests { + + /** + * @see DATAKV-101 + */ + @Test + public void toListShouldReturnEmptyListWhenSourceIsNull() { + assertThat(toList(null), notNullValue()); + } + + /** + * @see DATAKV-101 + */ + @Test + public void toListShouldReturnEmptyListWhenSourceEmpty() { + assertThat(toList(Collections.emptySet()), empty()); + } + + /** + * @see DATAKV-101 + */ + @Test + public void toListShouldReturnSameObjectWhenSourceIsAlreadyListType() { + + List source = new ArrayList(); + + assertThat(toList(source), sameInstance(source)); + } + + /** + * @see DATAKV-101 + */ + @Test + public void toListShouldReturnListWhenSourceIsNonListType() { + + Set source = new HashSet(); + source.add("tyrion"); + + assertThat(toList(source), instanceOf(List.class)); + } + + /** + * @see DATAKV-101 + */ + @Test + public void toListShouldHoldValuesInOrderOfSource() { + + Set source = new LinkedHashSet(); + source.add("tyrion"); + source.add("jaime"); + + assertThat(toList(source), contains(source.toArray(new String[2]))); + } + +} diff --git a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java index 5402f860..80e183c6 100644 --- a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java @@ -362,11 +362,9 @@ public void countShouldReturnZeroWhenNoElementsPresent() { * @see DATACMNS-525 */ @Test - @SuppressWarnings({ "rawtypes", "unchecked" }) public void countShouldReturnCollectionSize() { - Collection foo = Arrays.asList(FOO_ONE, FOO_ONE); - when(adapterMock.getAllOf(Foo.class.getName())).thenReturn(foo); + when(adapterMock.count(Foo.class.getName())).thenReturn(2L); assertThat(template.count(Foo.class), is(2L)); }