DATAKV-101 - Favor Iterable over Collection types for KeyValueOperations.

Change return types for Adapter and Operations from Collection types to Iterable. Added count(keyspace) to KeyValueAdapter.

Original pull request: #8.
This commit is contained in:
Christoph Strobl
2015-05-08 09:30:31 +02:00
committed by Thomas Darimont
parent 16dc4b5e7d
commit 952a901164
10 changed files with 198 additions and 36 deletions

View File

@@ -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 <T>
*/
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 <T> List<T> toList(Iterable<T> source) {
if (source == null) {
return Collections.emptyList();
}
if (source instanceof List) {
return (List<T>) source;
}
if (source instanceof Collection) {
return new ArrayList<T>((Collection<T>) source);
}
List<T> result = new ArrayList<T>();
for (T value : source) {
result.add(value);
}
return result;
}
}

View File

@@ -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}.

View File

@@ -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.
*/
<T> List<T> findAll(Class<T> type);
<T> Iterable<T> findAll(Class<T> 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
*/
<T> List<T> findAll(Sort sort, Class<T> type);
<T> Iterable<T> findAll(Sort sort, Class<T> 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.
*/
<T> List<T> find(KeyValueQuery<?> query, Class<T> type);
<T> Iterable<T> find(KeyValueQuery<?> query, Class<T> 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
*/
<T> List<T> findInRange(int offset, int rows, Class<T> type);
<T> Iterable<T> findInRange(int offset, int rows, Class<T> 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
*/
<T> List<T> findInRange(int offset, int rows, Sort sort, Class<T> type);
<T> Iterable<T> findInRange(int offset, int rows, Sort sort, Class<T> type);
/**
* @param objectToUpdate must not be {@literal null}.

View File

@@ -19,7 +19,6 @@ import static org.springframework.data.keyvalue.core.KeySpaceUtils.*;
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 class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
@Override
public List<T> doInKeyValue(KeyValueAdapter adapter) {
Collection<?> x = adapter.getAllOf(resolveKeySpace(type));
Iterable<?> values = adapter.getAllOf(resolveKeySpace(type));
if (getKeySpace(type) == null) {
return new ArrayList<T>((Collection<T>) x);
return new ArrayList<T>(IterableConverter.toList((Iterable<T>) values));
}
ArrayList<T> filtered = new ArrayList<T>();
for (Object candidate : x) {
for (Object candidate : values) {
if (typeCheck(type, candidate)) {
filtered.add((T) candidate);
}
@@ -332,7 +331,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
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 class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
@Override
public List<T> doInKeyValue(KeyValueAdapter adapter) {
Collection<?> result = adapter.find(query, resolveKeySpace(type));
Iterable<?> result = adapter.find(query, resolveKeySpace(type));
if (getKeySpace(type) == null) {
return new ArrayList<T>((Collection<T>) result);
return new ArrayList<T>(IterableConverter.toList((Iterable<T>) result));
}
List<T> filtered = new ArrayList<T>();
@@ -517,7 +516,7 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationContextA
}
private void potentiallyPublishEvent(KeyValueEvent event) {
if (eventPublisher == null) {
return;
}

View File

@@ -65,10 +65,10 @@ class SpelQueryEngine<T extends KeyValueAdapter> extends QueryEngine<KeyValueAda
}
@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);
}

View File

@@ -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.data.repository.query.parser.PartTree;
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 class KeyValuePartTreeQuery implements RepositoryQuery {
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 class KeyValuePartTreeQuery implements RepositoryQuery {
} 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;
}

View File

@@ -23,6 +23,7 @@ import org.springframework.data.domain.Page;
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 class SimpleKeyValueRepository<T, ID extends Serializable> implements Key
return new PageImpl<T>(result, null, result.size());
}
List<T> content = null;
content = operations.findInRange(pageable.getOffset(), pageable.getPageSize(), pageable.getSort(),
Iterable<T> content = operations.findInRange(pageable.getOffset(), pageable.getPageSize(), pageable.getSort(),
entityInformation.getJavaType());
return new PageImpl<T>(content, pageable, this.operations.count(entityInformation.getJavaType()));
return new PageImpl<T>(IterableConverter.toList(content), pageable, this.operations.count(entityInformation
.getJavaType()));
}
/*
@@ -139,7 +139,7 @@ public class SimpleKeyValueRepository<T, ID extends Serializable> implements Key
*/
@Override
public List<T> findAll() {
return operations.findAll(entityInformation.getJavaType());
return IterableConverter.toList(operations.findAll(entityInformation.getJavaType()));
}
/*

View File

@@ -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 class MapKeyValueAdapter extends AbstractKeyValueAdapter {
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)

View File

@@ -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<String> source = new ArrayList<String>();
assertThat(toList(source), sameInstance(source));
}
/**
* @see DATAKV-101
*/
@Test
public void toListShouldReturnListWhenSourceIsNonListType() {
Set<String> source = new HashSet<String>();
source.add("tyrion");
assertThat(toList(source), instanceOf(List.class));
}
/**
* @see DATAKV-101
*/
@Test
public void toListShouldHoldValuesInOrderOfSource() {
Set<String> source = new LinkedHashSet<String>();
source.add("tyrion");
source.add("jaime");
assertThat(toList(source), contains(source.toArray(new String[2])));
}
}

View File

@@ -362,11 +362,9 @@ public class KeyValueTemplateUnitTests {
* @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));
}