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 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 entityClass) { @@ -80,8 +85,10 @@ static ColumnReader forClass(Class 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 {