diff --git a/server/src/main/java/io/spine/server/entity/storage/ColumnReader.java b/server/src/main/java/io/spine/server/entity/storage/ColumnReader.java
index 21b2f65739b..c769b35f696 100644
--- a/server/src/main/java/io/spine/server/entity/storage/ColumnReader.java
+++ b/server/src/main/java/io/spine/server/entity/storage/ColumnReader.java
@@ -20,6 +20,7 @@
package io.spine.server.entity.storage;
+import com.google.common.collect.ImmutableSet;
import io.spine.server.entity.Entity;
import java.beans.BeanInfo;
@@ -27,9 +28,12 @@
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Objects;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Lists.newLinkedList;
import static io.spine.server.entity.storage.Methods.getAnnotatedVersion;
import static io.spine.util.Exceptions.newIllegalStateException;
@@ -63,7 +67,8 @@ private ColumnReader(Class extends Entity> entityClass) {
*
The reader can be further used to {@linkplain ColumnReader#readColumns() obtain}
* {@linkplain EntityColumn entity columns} for the given class.
*
- * @param entityClass {@link Entity} class for which to create the instance
+ * @param entityClass
+ * {@link Entity} class for which to create the instance
* @return new instance of {@code ColumnReader} for the specified class
*/
static ColumnReader forClass(Class extends Entity> entityClass) {
@@ -80,8 +85,10 @@ static ColumnReader forClass(Class extends Entity> entityClass) {
*
If the check for correctness fails, throws {@link IllegalStateException}.
*
* @return a {@code Collection} of {@link EntityColumn} corresponded to entity class
- * @throws IllegalStateException if entity column definitions are incorrect
+ * @throws IllegalStateException
+ * if entity column definitions are incorrect
*/
+ @SuppressWarnings("ConstantConditions")
Collection readColumns() {
BeanInfo entityDescriptor;
try {
@@ -90,28 +97,31 @@ Collection readColumns() {
throw new IllegalStateException(e);
}
- Collection entityColumns = newLinkedList();
- for (PropertyDescriptor property : entityDescriptor.getPropertyDescriptors()) {
- Method getter = property.getReadMethod();
- boolean isEntityColumn = getAnnotatedVersion(getter).isPresent();
- if (isEntityColumn) {
- EntityColumn column = EntityColumn.from(getter);
- entityColumns.add(column);
- }
- }
-
- checkRepeatedColumnNames(entityColumns);
- return entityColumns;
+ PropertyDescriptor[] propertyDescriptors = entityDescriptor.getPropertyDescriptors();
+ ImmutableSet columns = Arrays
+ .stream(propertyDescriptors)
+ .map(PropertyDescriptor::getReadMethod)
+ .filter(Objects::nonNull)
+ .filter(ColumnReader::isAnnotated)
+ .map(EntityColumn::from)
+ .collect(toImmutableSet());
+ checkRepeatedColumnNames(columns);
+ return columns;
}
+ private static boolean isAnnotated(Method method) {
+ return getAnnotatedVersion(method).isPresent();
+ }
/**
* Ensures that the specified columns have no repeated names.
*
* If the check fails, throws {@link IllegalStateException}.
*
- * @param columns the columns to check
- * @throws IllegalStateException if columns contain repeated names
+ * @param columns
+ * the columns to check
+ * @throws IllegalStateException
+ * if columns contain repeated names
*/
private void checkRepeatedColumnNames(Iterable columns) {
Collection checkedNames = newLinkedList();
diff --git a/server/src/test/java/io/spine/server/entity/storage/ColumnReaderTest.java b/server/src/test/java/io/spine/server/entity/storage/ColumnReaderTest.java
index 4de38c9010d..82ce580df7c 100644
--- a/server/src/test/java/io/spine/server/entity/storage/ColumnReaderTest.java
+++ b/server/src/test/java/io/spine/server/entity/storage/ColumnReaderTest.java
@@ -22,6 +22,7 @@
import com.google.common.testing.NullPointerTester;
import com.google.common.testing.NullPointerTester.Visibility;
+import io.spine.server.entity.storage.given.ColumnsTestEnv.EntityWithASetterButNoGetter;
import io.spine.server.entity.storage.given.ColumnsTestEnv.EntityWithColumnFromInterface;
import io.spine.server.entity.storage.given.ColumnsTestEnv.EntityWithManyGetters;
import io.spine.server.entity.storage.given.ColumnsTestEnv.EntityWithManyGettersDescendant;
@@ -114,6 +115,14 @@ void fromImplementedInterface() {
}
}
+ @Test
+ @DisplayName("not confuse a setter method with a property mutator")
+ void testSetterDeclaringEntity() {
+ ColumnReader columnReader = forClass(EntityWithASetterButNoGetter.class);
+ Collection entityColumns = columnReader.readColumns();
+ assertThat(entityColumns).isEmpty();
+ }
+
@Nested
@DisplayName("when extracting columns, ignore")
class Ignore {
diff --git a/server/src/test/java/io/spine/server/entity/storage/given/ColumnsTestEnv.java b/server/src/test/java/io/spine/server/entity/storage/given/ColumnsTestEnv.java
index f29c37187cc..6cd5d47b157 100644
--- a/server/src/test/java/io/spine/server/entity/storage/given/ColumnsTestEnv.java
+++ b/server/src/test/java/io/spine/server/entity/storage/given/ColumnsTestEnv.java
@@ -53,6 +53,28 @@ public int getValue() {
}
}
+ /**
+ * An entity type which declares a {@linkplain #setSecretNumber(Integer) mutator method},
+ * however doesn't declare a respective accessor method.
+ *
+ * {@code ColumnReader} should not get confused and assume that the mutator method is
+ * a property, and, therefore, a potential column.
+ */
+ @SuppressWarnings("unused") // Reflective access
+ public static class EntityWithASetterButNoGetter extends AbstractEntity {
+
+ private Integer secretNumber;
+
+ protected EntityWithASetterButNoGetter(String id) {
+ super(id);
+ }
+
+ @SuppressWarnings("WeakerAccess") // Required for a test
+ public void setSecretNumber(Integer secretNumber) {
+ this.secretNumber = secretNumber;
+ }
+ }
+
@SuppressWarnings("unused") // Reflective access
public static class EntityWithManyGetters extends AbstractEntity {