/*
 * Copyright 2021, TeamDev. All rights reserved.
 *
 * 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
 *
 * 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.entity.storage;

import com.google.errorprone.annotations.Immutable;
import io.spine.query.Column;
import io.spine.query.RecordColumn;
import io.spine.server.entity.EntityRecord;

import java.util.function.Function;

import static com.google.common.base.Preconditions.checkNotNull;
import static io.spine.query.RecordColumn.create;
import static io.spine.util.Exceptions.newIllegalStateException;

/**
 * Utility which creates views on the {@linkplain Column columns} posting them as instances
 * of {@link RecordColumn}s.
 *
 * <p>Such a "type cast" is required in order to adapt the all the column types available
 * to the framework users into the {@code RecordColumn}s, which are used in
 * {@linkplain io.spine.query.RecordQuery the common data structures} for
 * {@linkplain io.spine.server.storage.RecordStorage storage and retrieval} of the records data.
 */
final class AsEntityRecordColumn {

    /**
     * Prevents the instantiation of this utility.
     */
    private AsEntityRecordColumn() {
    }

    /**
     * Creates a view on the given column as on a record column of an {@link EntityRecord}
     * with the given type of values.
     *
     * <p>The resulting view loses an ability to {@linkplain Column#valueIn(Object) obtain
     * the values from the record instance}.
     *
     * @param original
     *         the column to create a view for
     * @param typeOfValues
     *         the type of the column values
     * @param <V>
     *         the type of the column values
     * @return a view on the column
     */
    @SuppressWarnings("BadImport")       // `create` looks fine in this context.
    static <V> RecordColumn<EntityRecord, V> apply(Column<?, ?> original, Class<V> typeOfValues) {
        return apply(original, typeOfValues, new NoGetter<>());
    }

    /**
     * Creates a view on the given column as on a record column of an {@link EntityRecord}
     * with the given type of values.
     *
     * <p>A value of the resulting column is determined by the passed getter.
     *
     * @param original
     *         the column to create a view for
     * @param typeOfValues
     *         the type of the column values
     * @param getter
     *         the getter returning the value of the resulting column view
     * @param <V>
     *         the type of the column values
     * @return a view on the column
     */
    @SuppressWarnings("BadImport")       // `create` looks fine in this context.
    static <V> RecordColumn<EntityRecord, V>
    apply(Column<?, ?> original, Class<V> typeOfValues, Function<EntityRecord, V> getter) {
        checkNotNull(original);
        checkNotNull(typeOfValues);
        String columnName = columnName(original);
        return create(columnName, typeOfValues, getter::apply);
    }

    /**
     * Creates a view on the given column as on the record column of an {@linkplain EntityRecord}
     * with the {@code Object} values.
     *
     * <p>The resulting view loses an ability to {@linkplain Column#valueIn(Object) obtain
     * the values from the record instance}.
     *
     * @param original
     *         the column to create a view for
     * @return a view on the column
     */
    static RecordColumn<EntityRecord, Object> apply(Column<?, ?> original) {
        return apply(original, Object.class);
    }

    private static String columnName(Column<?, ?> original) {
        return original.name()
                       .value();
    }

    /**
     * Returns the getter which always throws an {@link IllegalStateException} upon invocation.
     */
    @Immutable
    private static final class NoGetter<V> implements Column.Getter<EntityRecord, V> {

        @Override
        public V apply(EntityRecord record) {
            throw newIllegalStateException("`EntityRecordColumn`s do not have getters.");
        }
    }
}

@@ -1546,58 +1546,8 @@
 ll;%0A
-import static io.spine.query.RecordColumn.create;%0A
 impo
@@ -1607,16 +1607,16 @@
 eption;%0A
+
 %0A/**%0A * 
@@ -2228,795 +2228,8 @@
  %7D%0A%0A
-    /**%0A     * Creates a view on the given column as on a record column of an %7B@link EntityRecord%7D%0A     * with the given type of values.%0A     *%0A     * %3Cp%3EThe resulting view loses an ability to %7B@linkplain Column#valueIn(Object) obtain%0A     * the values from the record instance%7D.%0A     *%0A     * @param original%0A     *         the column to create a view for%0A     * @param typeOfValues%0A     *         the type of the column values%0A     * @param %3CV%3E%0A     *         the type of the column values%0A     * @return a view on the column%0A     */%0A    @SuppressWarnings(%22BadImport%22)       // %60create%60 looks fine in this context.%0A    static %3CV%3E RecordColumn%3CEntityRecord, V%3E apply(Column%3C?, ?%3E original, Class%3CV%3E typeOfValues) %7B%0A        return apply(original, typeOfValues, new NoGetter%3C%3E());%0A    %7D%0A%0A
     
@@ -2802,89 +2802,8 @@
  */%0A
-    @SuppressWarnings(%22BadImport%22)       // %60create%60 looks fine in this context.%0A
     
@@ -3068,16 +3068,29 @@
  return 
+RecordColumn.
 create(c
@@ -3672,24 +3672,742 @@
 ss);%0A    %7D%0A%0A
+    /**%0A     * Creates a view on the given column as on a record column of an %7B@link EntityRecord%7D%0A     * with the given type of values.%0A     *%0A     * %3Cp%3EThe resulting view loses an ability to %7B@linkplain Column#valueIn(Object) obtain%0A     * the values from the record instance%7D.%0A     *%0A     * @param original%0A     *         the column to create a view for%0A     * @param typeOfValues%0A     *         the type of the column values%0A     * @param %3CV%3E%0A     *         the type of the column values%0A     * @return a view on the column%0A     */%0A    private static %3CV%3E RecordColumn%3CEntityRecord, V%3E%0A    apply(Column%3C?, ?%3E original, Class%3CV%3E typeOfValues) %7B%0A        return apply(original, typeOfValues, new NoGetter%3C%3E());%0A    %7D%0A%0A
     private 
@@ -4520,23 +4520,22 @@
 alue();%0A
+
     %7D%0A
-%0A
     /**%0A

Remove the `BadImport` suppression along with the code which caused it. Re-order the methods and make one of them `private`, as it's not used from outside.