SolutionAspect.java

/*
 * Copyright (c) 2021 Mārtiņš Avots (Martins Avots) and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the MIT License,
 * which is available at https://spdx.org/licenses/MIT.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR MIT
 */
package net.splitcells.gel.solution;

import net.splitcells.dem.data.set.Set;
import net.splitcells.dem.data.set.list.List;
import net.splitcells.dem.data.set.list.ListView;
import net.splitcells.dem.environment.config.StaticFlags;
import net.splitcells.dem.lang.annotations.ReturnsThis;
import net.splitcells.dem.resource.communication.interaction.LogLevel;
import net.splitcells.gel.constraint.Constraint;
import net.splitcells.gel.data.allocation.Allocations;
import net.splitcells.gel.data.database.AfterAdditionSubscriber;
import net.splitcells.gel.data.database.BeforeRemovalSubscriber;
import net.splitcells.gel.data.database.Database;
import net.splitcells.gel.data.table.Line;
import net.splitcells.gel.data.table.LinePointer;
import net.splitcells.gel.data.table.attribute.Attribute;
import net.splitcells.gel.data.table.column.Column;
import net.splitcells.gel.data.table.column.ColumnView;
import net.splitcells.gel.problem.derived.DerivedSolution;
import net.splitcells.gel.rating.framework.MetaRating;
import net.splitcells.gel.rating.framework.Rating;
import net.splitcells.gel.rating.type.Cost;
import net.splitcells.gel.solution.history.History;
import net.splitcells.gel.solution.optimization.OptimizationEvent;
import org.w3c.dom.Node;

import java.util.function.Function;

import static net.splitcells.dem.data.set.list.Lists.list;
import static net.splitcells.dem.lang.perspective.PerspectiveI.perspective;
import static net.splitcells.dem.resource.communication.log.Domsole.domsole;
import static net.splitcells.gel.common.Language.RATING;
import static net.splitcells.gel.solution.optimization.StepType.REMOVAL;
import static org.assertj.core.api.Assertions.assertThat;

public class SolutionAspect implements Solution {
    public static SolutionAspect solutionAspect(Solution solution) {
        return new SolutionAspect(solution);
    }

    private final Solution solution;

    private SolutionAspect(Solution solution) {
        this.solution = solution;
    }

    @Override
    public Solution optimize(List<OptimizationEvent> events) {
        final var solution = Solution.super.optimize(events);
        if (StaticFlags.TRACING) {
            domsole().append
                    (constraint().rating()
                            , () -> solution.path().withAppended(RATING.value())
                            , LogLevel.TRACE);
        }
        return solution;
    }

    @Override
    public Rating rating(List<OptimizationEvent> event) {
        final var rating = Solution.super.rating(event);
        if (StaticFlags.TRACING) {
            domsole().append
                    (rating
                            , () -> solution.path().withAppended(RATING.value())
                            , LogLevel.TRACE);
        }
        return rating;
    }

    @ReturnsThis
    public Solution optimize(OptimizationEvent event, OptimizationParameters parameters) {
        if (event.demand().interpret().isEmpty()) {
            throw new IllegalArgumentException("Unknown demand: " + event.demand().index() + ", " + event.demand().context().path());
        }
        if (event.supply().interpret().isEmpty()) {
            throw new IllegalArgumentException("Unknown supply: " + event.supply().index() + ", " + event.supply().context().path());
        }
        if (event.stepType().equals(REMOVAL)) {
            event.demand()
                    .interpret()
                    .ifPresent(demandsForRemoval
                            -> assertThat(list(demandsForRemoval.context()))
                            .containsAnyOf(solution.demands(), solution.demandsUsed()));
            event.supply()
                    .interpret()
                    .ifPresent(supplyForRemoval
                            -> assertThat(list(supplyForRemoval.context()))
                            .containsAnyOf(solution.supplies(), solution.suppliesUsed()));
        }
        final var result = solution.optimize(event, parameters);
        if (StaticFlags.TELLING_STORY) {
            domsole().append(perspective("" + constraint().rating().asMetaRating().getContentValue(Cost.class).value())
                    , () -> path().withAppended("optimize", "after", "cost")
                    , LogLevel.DEBUG);
            if (isComplete()) {
                domsole().append(perspective(this.history().size() + ", " + constraint().rating().asMetaRating().getContentValue(Cost.class).value())
                        , () -> path().withAppended("isComplete", "optimize", "after", "cost")
                        , LogLevel.DEBUG);
            }
        }
        return result;
    }

    @Override
    public History history() {
        return solution.history();
    }

    @Override
    public Solution asSolution() {
        return solution.asSolution();
    }

    @Override
    public Line allocate(Line demand, Line supply) {
        return solution.allocate(demand, supply);
    }

    @Override
    public Line allocationOf(LinePointer demand, LinePointer supply) {
        return solution.allocationOf(demand, supply);
    }

    @Override
    public Line addTranslated(List<?> values) {
        return solution.addTranslated(values);
    }

    @Override
    public Line add(Line line) {
        return solution.add(line);
    }

    @Override
    public void remove(int lineIndex) {
        solution.remove(lineIndex);
    }

    @Override
    public void remove(Line line) {
        solution.remove(line);
    }

    @Override
    public void subscribeToAfterAdditions(AfterAdditionSubscriber subscriber) {
        solution.subscribeToAfterAdditions(subscriber);
    }

    @Override
    public void subscribeToBeforeRemoval(BeforeRemovalSubscriber subscriber) {
        solution.subscribeToBeforeRemoval(subscriber);
    }

    @Override
    public void subscribeToAfterRemoval(BeforeRemovalSubscriber subscriber) {
        solution.subscribeToAfterRemoval(subscriber);
    }

    @Override
    public Constraint constraint() {
        return solution.constraint();
    }

    @Override
    public Allocations allocations() {
        return solution.allocations();
    }

    @Override
    public DerivedSolution derived(Function<Rating, Rating> derivation) {
        return solution.derived(derivation);
    }

    @Override
    public Database supplies() {
        return solution.supplies();
    }

    @Override
    public Database suppliesUsed() {
        return solution.suppliesUsed();
    }

    @Override
    public Database suppliesFree() {
        return solution.suppliesFree();
    }

    @Override
    public Database demands() {
        return solution.demands();
    }

    @Override
    public Database demandsUsed() {
        return solution.demandsUsed();
    }

    @Override
    public Database demandsFree() {
        return solution.demandsFree();
    }

    @Override
    public Line demandOfAllocation(Line allocation) {
        return solution.demandOfAllocation(allocation);
    }

    @Override
    public Line supplyOfAllocation(Line allocation) {
        return solution.supplyOfAllocation(allocation);
    }

    @Override
    public Set<Line> allocationsOfSupply(Line supply) {
        return solution.allocationsOfSupply(supply);
    }

    @Override
    public Set<Line> allocationsOfDemand(Line demand) {
        return solution.allocationsOfDemand(demand);
    }

    @Override
    public List<Attribute<Object>> headerView() {
        return solution.headerView();
    }

    @Override
    public <T> ColumnView<T> columnView(Attribute<T> attribute) {
        return solution.columnView(attribute);
    }

    @Override
    public List<Column<Object>> columnsView() {
        return solution.columnsView();
    }

    @Override
    public ListView<Line> rawLinesView() {
        return solution.rawLinesView();
    }

    @Override
    public int size() {
        return solution.size();
    }

    @Override
    public List<Line> rawLines() {
        return solution.rawLines();
    }

    @Override
    public Line lookupEquals(Attribute<Line> attribute, Line value) {
        return solution.lookupEquals(attribute, value);
    }

    @Override
    public Node toDom() {
        return solution.toDom();
    }

    @Override
    public List<String> path() {
        return solution.path();
    }
}