giedomak/Telepath

View on GitHub
src/main/java/com/github/giedomak/telepath/datamodels/plans/utilities/MultiTreeFlattener.kt

Summary

Maintainability
A
0 mins
Test Coverage
/**
 * Copyright (C) 2016-2017 - All rights reserved.
 * This file is part of the telepath project which is released under the GPLv3 license.
 * See file LICENSE.txt or go to http: *www.gnu.org/licenses/gpl.txt for full license details.
 * You may use, distribute and modify this code under the terms of the GPLv3 license.
 */

package com.github.giedomak.telepath.datamodels.plans.utilities

import com.github.giedomak.telepath.datamodels.plans.AbstractMultiTree

/**
 * Flatten ParseTrees into multi-trees.
 *
 * We escalate the children from a node to its parent if both the node and its parent have the same operator.
 *
 * Example input:
 *
 *            CONCATENATION
 *          /       |        \
 *         a  CONCATENATION   f
 *              /      \
 *          UNION       e
 *         /    \
 *        b    UNION
 *             /  \
 *            c    d
 *
 * Output:
 *
 *        CONCATENATION
 *        /   |    |  \
 *       a  UNION  e   f
 *          / | \
 *         b  c  d
 */
object MultiTreeFlattener {

    /**
     * Flatten a given LogicalPlan recursively into a multi-tree.
     *
     * @param tree The given input LogicalPlan.
     * @return A flattened LogicalPlan from the given input.
     */
    fun <ImplementingTree : AbstractMultiTree<ImplementingTree>> flatten(tree: ImplementingTree): ImplementingTree {

        // Break recursion once we've found a leaf.
        if (tree.isLeaf) return tree

        // Since we could be escalating multiple children from the same parent, we have to track these escalations.
        var offset = 0

        for (index in tree.children.indices) {

            // Flatten each child recursively
            val child = tree.children[index + offset]
            val flattenedChild = flatten(child)

            // The subtree rooted at flattenedChild, should all be flattened now.
            // So escalate its children if the operators match.
            if (child.operator == tree.operator) {

                tree.children.removeAt(index + offset)
                tree.children.addAll(index + offset, flattenedChild.children)

                offset += flattenedChild.children.size - 1
            }
        }

        // We're done
        return tree
    }
}