NodeCalcDuplicateDetector.java

/*
 * Copyright (c) 2024, 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.timeseries.ast;

import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author Nicolas Rol {@literal <nicolas.rol at rte-france.com>}
 */
public class NodeCalcDuplicateDetector extends NodeCalcModifier<Map<NodeCalc, Set<NodeCalc>>> {

    /**
     * Create a set of parents for each node in the tree that could be cached.
     * @param nodeCalc Head of the node tree
     * @return Map of the parents for each node in the tree
     */
    public static Map<NodeCalc, Set<NodeCalc>> detectDuplicates(NodeCalc nodeCalc) {
        return new NodeCalcDuplicateDetector().detect(nodeCalc);
    }

    private Map<NodeCalc, Set<NodeCalc>> detect(NodeCalc nodeCalc) {
        Map<NodeCalc, Set<NodeCalc>> parents = new IdentityHashMap<>(100);
        nodeCalc.accept(this, parents, 0);
        return parents;
    }

    @Override
    public NodeCalc visit(BinaryOperation nodeCalc, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc left, NodeCalc right) {
        return visitBinaryNodeCalc(nodeCalc, parents, left, right);
    }

    @Override
    public NodeCalc visit(BinaryMinCalc nodeCalc, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc left, NodeCalc right) {
        return visitBinaryNodeCalc(nodeCalc, parents, left, right);
    }

    @Override
    public NodeCalc visit(BinaryMaxCalc nodeCalc, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc left, NodeCalc right) {
        return visitBinaryNodeCalc(nodeCalc, parents, left, right);
    }

    @Override
    public NodeCalc visit(UnaryOperation nodeCalc, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc child) {
        return visitSingleChildNodeCalc(nodeCalc, parents, child);
    }

    @Override
    public NodeCalc visit(MinNodeCalc nodeCalc, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc child) {
        return visitSingleChildNodeCalc(nodeCalc, parents, child);
    }

    @Override
    public NodeCalc visit(MaxNodeCalc nodeCalc, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc child) {
        return visitSingleChildNodeCalc(nodeCalc, parents, child);
    }

    @Override
    public NodeCalc visit(TimeNodeCalc nodeCalc, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc child) {
        return visitSingleChildNodeCalc(nodeCalc, parents, child);
    }

    @Override
    public NodeCalc visit(CachedNodeCalc nodeCalc, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc child) {
        visitSingleChildNodeCalc(nodeCalc, parents, child);
        return null;
    }

    private void visitNodeCalc(NodeCalc parent, NodeCalc child, Map<NodeCalc, Set<NodeCalc>> parents) {
        if (parents.containsKey(child)) {
            // If the child is already in the map, the parent is added to its set
            parents.get(child).add(parent);
        } else {
            // If the child is not already in the map, is is added and a set based on an IdentityHashMap (in order to
            // use the identity comparison) is created for this child, with the parent in it.
            Set<NodeCalc> nodeCalcParents = Collections.newSetFromMap(new IdentityHashMap<>());
            nodeCalcParents.add(parent);
            parents.put(child, nodeCalcParents);
        }
    }

    private NodeCalc visitBinaryNodeCalc(AbstractBinaryNodeCalc parent, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc left, NodeCalc right) {
        if (left != null) {
            visitNodeCalc(parent, left, parents);
        }
        if (right != null) {
            visitNodeCalc(parent, right, parents);
        }
        return parent;
    }

    private NodeCalc visitSingleChildNodeCalc(AbstractSingleChildNodeCalc parent, Map<NodeCalc, Set<NodeCalc>> parents, NodeCalc child) {
        if (child != null) {
            visitNodeCalc(parent, child, parents);
        }
        return parent;
    }

}