SlackTerminalImpl.java

/**
 * Copyright (c) 2020, RTE (http://www.rte-france.com)
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * SPDX-License-Identifier: MPL-2.0
 */
package com.powsybl.iidm.network.impl.extensions;

import com.powsybl.commons.PowsyblException;
import com.powsybl.iidm.network.Terminal;
import com.powsybl.iidm.network.VoltageLevel;
import com.powsybl.iidm.network.extensions.SlackTerminal;
import com.powsybl.iidm.network.impl.AbstractMultiVariantIdentifiableExtension;
import com.powsybl.iidm.network.impl.TerminalExt;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;

/**
 * @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
 */
public class SlackTerminalImpl extends AbstractMultiVariantIdentifiableExtension<VoltageLevel> implements SlackTerminal {

    private final ArrayList<Terminal> terminals;

    SlackTerminalImpl(VoltageLevel voltageLevel, Terminal terminal) {
        super(voltageLevel);
        this.terminals = new ArrayList<>(
            Collections.nCopies(getVariantManagerHolder().getVariantManager().getVariantArraySize(), null));
        this.setTerminal(terminal);
    }

    private void unregisterReferencedTerminalIfNeeded(int variantIndex) {
        // check there is no more same terminal referenced by any variant, unregister it
        Terminal oldTerminal = terminals.get(variantIndex);
        if (oldTerminal != null && Collections.frequency(terminals, oldTerminal) == 1) {
            ((TerminalExt) oldTerminal).getReferrerManager().unregister(this);
        }
    }

    private void registerReferencedTerminalIfNeeded(Terminal terminal) {
        // if terminal was not already referenced by another variant, register it
        if (terminal != null && !terminals.contains(terminal)) {
            ((TerminalExt) terminal).getReferrerManager().register(this);
        }
    }

    private void setTerminalAndUpdateReferences(int variantIndex, Terminal terminal) {
        unregisterReferencedTerminalIfNeeded(variantIndex);
        registerReferencedTerminalIfNeeded(terminal);
        terminals.set(variantIndex, terminal);
    }

    private void addTerminalAndUpdateReferences(Terminal terminal) {
        registerReferencedTerminalIfNeeded(terminal);
        terminals.add(terminal);
    }

    private void removeTerminalAndUpdateReferences(int variantIndex) {
        unregisterReferencedTerminalIfNeeded(variantIndex);
        terminals.remove(variantIndex);
    }

    @Override
    public Terminal getTerminal() {
        return terminals.get(getVariantIndex());
    }

    @Override
    public SlackTerminal setTerminal(Terminal terminal) {
        if (terminal != null && !terminal.getVoltageLevel().equals(getExtendable())) {
            throw new PowsyblException("Terminal given is not in the right VoltageLevel ("
                + terminal.getVoltageLevel().getId() + " instead of " + getExtendable().getId() + ")");
        }
        setTerminalAndUpdateReferences(getVariantIndex(), terminal);
        return this;
    }

    @Override
    public boolean isEmpty() {
        return terminals.stream().noneMatch(Objects::nonNull);
    }

    @Override
    public void extendVariantArraySize(int initVariantArraySize, int number, int sourceIndex) {
        terminals.ensureCapacity(terminals.size() + number);
        Terminal sourceTerminal = terminals.get(sourceIndex);
        for (int i = 0; i < number; ++i) {
            addTerminalAndUpdateReferences(sourceTerminal);
        }
    }

    @Override
    public void reduceVariantArraySize(int number) {
        for (int i = 0; i < number; i++) {
            removeTerminalAndUpdateReferences(terminals.size() - 1); // remove elements from the top to avoid moves inside the array
        }
    }

    @Override
    public void deleteVariantArrayElement(int index) {
        setTerminalAndUpdateReferences(index, null);
    }

    @Override
    public void allocateVariantArrayElement(int[] indexes, int sourceIndex) {
        Terminal terminalSource = terminals.get(sourceIndex);
        for (int index : indexes) {
            setTerminalAndUpdateReferences(index, terminalSource);
        }
    }

    @Override
    public void onReferencedRemoval(Terminal removedTerminal) {
        int variantIndex = terminals.indexOf(removedTerminal);
        if (variantIndex != -1) {
            terminals.set(variantIndex, null);
        }
    }

    @Override
    public void onReferencedReplacement(Terminal oldReferenced, Terminal newReferenced) {
        terminals.replaceAll(t -> t == oldReferenced ? newReferenced : t);
    }

    @Override
    public void cleanup() {
        for (int i = 0; i < terminals.size(); i++) {
            Terminal terminal = terminals.get(i);
            if (terminal != null) {
                ((TerminalExt) terminal).getReferrerManager().unregister(this);
                terminals.set(i, null);
            }
        }
    }
}