TerminalFinder.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.util;

import com.powsybl.iidm.network.*;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * A class which allows to find the best terminal, according to given comparator or list of predicate, among several
 * terminals.
 * @author Florian Dupuy {@literal <florian.dupuy at rte-france.com>}
 */
public final class TerminalFinder {

    private final Comparator<Terminal> comparator;

    public static TerminalFinder getDefault() {
        return new TerminalFinder(getDefaultRules());
    }

    public TerminalFinder(Comparator<Terminal> comparator) {
        this.comparator = Objects.requireNonNull(comparator);
    }

    /**
     * Constructs a TerminalFinder based on the given list of predicates, which defines a comparator based on the
     * following rules: a terminal t1 is better than a terminal t2 if the first predicate in the list on which t1 is
     * satisfied is before the first predicate in the list on which t2 is satisfied.
     * @param predicates the list of predicates
     */
    public TerminalFinder(List<Predicate<Terminal>> predicates) {
        this.comparator = getComparator(predicates);
    }

    /**
     * @param terminals the terminals among which a terminal has to be chosen
     * @return a terminal among the best ones based on the comparator field
     */
    public Optional<? extends Terminal> find(Iterable<? extends Terminal> terminals) {
        return find(StreamSupport.stream(terminals.spliterator(), false));
    }

    /**
     * @param terminals the terminals among which a terminal has to be chosen
     * @return a terminal among the best ones based on the comparator field
     */
    public Optional<? extends Terminal> find(Stream<? extends Terminal> terminals) {
        return terminals.max(comparator);
    }

    /**
     * Constructs a comparator based on a list of predicates, based on the following: a terminal t1 is better than a
     * terminal t2 if the first predicate in the list on which t1 is satisfied is before the first predicate in the list
     * on which t2 is satisfied.
     * @param predicates the list of predicates on which the comparator is based
     * @return the corresponding comparator
     */
    private static Comparator<Terminal> getComparator(List<Predicate<Terminal>> predicates) {
        Objects.requireNonNull(predicates);
        return (t1, t2) -> {
            if (t1.equals(t2)) {
                return 0;
            }
            // Looping over the predicates to find the first predicate which is satisfied on t1 or t2
            for (Predicate<Terminal> p : predicates) {
                if (p.test(t1)) {
                    return 1; // t1 is better as t2 didn't satisfy any previous predicate (t1 is then considered better even if t2 satisfies current predicate)
                } else if (p.test(t2)) {
                    return -1;  // t2 is better as t1 didn't satisfy current predicate nor any previous predicate
                }
            }
            return 1; // no predicate is verified by t1 or t2, the first is then considered better than the latter
        };
    }

    private static List<Predicate<Terminal>> getDefaultRules() {
        List<Predicate<Terminal>> rules = new ArrayList<>();
        rules.add(t -> t.getConnectable() instanceof BusbarSection);
        rules.add(t -> t.getConnectable() instanceof Injection);
        rules.add(t -> t.getConnectable() instanceof Line);
        rules.add(t -> t.getConnectable() instanceof TwoWindingsTransformer);
        rules.add(t -> t.getConnectable() instanceof ThreeWindingsTransformer);
        rules.add(t -> t.getConnectable() instanceof HvdcConverterStation);
        rules.add(Objects::nonNull);
        return rules;
    }
}