/*
 * Copyright 2020, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.server.storage;

import com.google.errorprone.annotations.Immutable;
import com.google.protobuf.Message;
import io.spine.annotation.Internal;
import io.spine.server.entity.storage.ColumnName;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.function.Function;

/**
 * A column to store along with a message record.
 *
 * <p>The value of the column is determined by a {@linkplain Getter getter} and should be extracted
 * from the record fields.
 *
 * <p>There are some other types of the columns are defined for the {@code Entity} state
 * in a declarative way, e.g. in the Protobuf definition of the {@code Entity} state or via
 * the {@code Entity}'s Java interface. The {@code CustomColumn}s are only intended to be used
 * along with the message records which do not represent an {@code EntityState}.
 *
 * <p>{@code CustomColumn} is the only way to programmatically specify the columns to be stored
 * along with a plain Protobuf message.
 *
 * @param <V>
 *         the type of the column value
 * @param <M>
 *         the type of the message record, along with which this column should be stored
 */
@Immutable
@Internal
public final class CustomColumn<V, M extends Message> extends AbstractColumn {

    private final Getter<M, V> getter;

    public CustomColumn(ColumnName name, Class<V> type, Getter<M, V> getter) {
        super(name, type);
        this.getter = getter;
    }

    public @Nullable V valueIn(M record) {
        return getter.apply(record);
    }

    @Immutable
    @FunctionalInterface
    public interface Getter<M extends Message, V> extends Function<M, V> {

    }
}

@@ -1548,20 +1548,16 @@
 columns 
-are 
 defined 
@@ -1560,24 +1560,43 @@
 ned for the 
+stored state of an 
 %7B@code Entit
@@ -1597,22 +1597,16 @@
  Entity%7D
- state
 %0A * in a
@@ -1625,14 +1625,30 @@
  way
-, e.g.
+. They may be defined 
  in 
@@ -1702,18 +1702,18 @@
 tate
+%0A *
  or via
-%0A *
  the
@@ -1745,18 +1745,40 @@
 terface.
- T
+%0A *%0A * %3Cp%3EUnlike them, t
 he %7B@cod
@@ -1822,19 +1822,16 @@
  be used
-%0A *
  along w
@@ -1834,31 +1834,48 @@
 ng with the 
-m
+plain%0A * %7B@code M
 essage
+%7D
  records whi
@@ -1875,33 +1875,31 @@
 rds 
-which do not represent an
+and not for storing the
  %7B@c
@@ -1912,45 +1912,44 @@
 tity
-S
+%7D s
 tate
-%7D.%0A *%0A * %3Cp%3E%7B@code CustomColumn%7D
+. The columns of this type%0A *
  is 
@@ -2017,47 +2017,26 @@
 ored
-%0A * along with a plain Protobuf message
+ with such records
 .%0A *
@@ -2346,233 +2346,884 @@
     
-public CustomColumn(ColumnName name, Class%3CV%3E type, Getter%3CM, V%3E getter) %7B%0A        super(name, type);%0A        this.getter = getter;%0A    %7D%0A%0A    public @Nullable V valueIn(M record) %7B%0A        return getter.apply(record);%0A    %7D%0A
+/**%0A     * Creates a new instance of a %7B@code CustomColumn%7D.%0A     *%0A     * @param name%0A     *         the name of the column%0A     * @param type%0A     *         the type of the column value%0A     * @param getter%0A     *         the getter for the column value%0A     */%0A    public CustomColumn(ColumnName name, Class%3CV%3E type, Getter%3CM, V%3E getter) %7B%0A        super(name, type);%0A        this.getter = getter;%0A    %7D%0A%0A    /**%0A     * Obtains the value of this column for the passed message record.%0A     *%0A     * @see Getter%0A     */%0A    public @Nullable V valueIn(M record) %7B%0A        return getter.apply(record);%0A    %7D%0A%0A    /**%0A     * A method object serving to obtain the value of the column for some particular record of the%0A     * matching type.%0A     *%0A     * @param %3CM%3E%0A     *         the type of the message record%0A     * @param %3CV%3E%0A     *         the type of the column value%0A     */
 %0A   
@@ -3334,17 +3334,16 @@
 M, V%3E %7B%0A
-%0A
     %7D%0A%7D%0A

Document the class.