All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tools.xor.view.SplitToRoot Maven / Gradle / Ivy

There is a newer version: 2.4.1
Show newest version
/**
 * XOR, empowering Model Driven Architecture in J2EE applications
 *
 * Copyright (c) 2019, Dilip Dalton
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and limitations
 * under the License.
 */

package tools.xor.view;

import tools.xor.util.InterQuery;
import tools.xor.util.IntraQuery;

import java.util.LinkedList;
import java.util.List;

/**
 * This strategy needs to be used if needing an OUTER JOIN type functionality
 * i.e., if the join needs to be included in the result, irrespective of whether
 */
public class SplitToRoot implements TreeMutatorStrategy
{
    private AggregateTree> aggregateTree;

    public SplitToRoot (AggregateTree aggregateTree) {
        this.aggregateTree = aggregateTree;
    }

    /**
     * 1. Loop through every QueryTree
     * 2. For each QueryTree, loop through every QueryFragment
     * 3. Rollup all the collections found in the descendants. At the end of this
     *    process, the root node will have a max of parallel collections in the tree.
     * 4. Do a BFS and if any node has more than 1 parallel collection, it represents a
     *    CartesianJoin and the QueryTree needs to be split into separate queries.
     *    Randomly choose a collection to split and create a new QueryTree out of it and
     *    add it to the end of the list of QueryTree to process.
     */
    @Override public void execute ()
    {
        List pieces = aggregateTree.getNonCustomVertices();
        while(!pieces.isEmpty()) {
            QueryTree> queryTree = pieces.remove(0);

            for(QueryFragment fragment: queryTree.getVertices()) {
                if(queryTree.getSubtypes(fragment).size() > 0) {
                    throw new RuntimeException("SplitToRoot not supported for fields on subtypes, use SplitToAnchor instead.");
                }
            }

            QueryFragment root = queryTree.getRoot();
            queryTree.computeCollectionCount(root);

            if(root.getParallelCollectionCount() > 1) {
                // There is a minimum of one feasible split
                List newQueries = processSplit(queryTree, root);

                // processSplit splits at the first occurrences of a parallel collection
                // The new queries could have further parallel collections so we need
                // to process them
                pieces.addAll(newQueries);
            }
        }
    }

    /**
     * The way this splitter works is we create a clone of the Fragment where the split
     * needs to occur and populate it with the edge that is split from the original QueryFragment
     * We then wrap it in a new QueryTree and connect it with an InterQuery edge.
     *
     * @param originalQT is the QueryTree that needs to be further split into additional queries
     * @param fragment is the root of the QueryTree
     * @return new queries split from the original QueryTree
     */
    private List processSplit(QueryTree> originalQT, QueryFragment fragment) {
        // first check if there are simple collections
        // if so, put each in a separate query

        List newQueries = new LinkedList<>();

        if(fragment.getSimpleCollectionCount() > 0) {
            // Find the simple collections
            for(String simpleColl: fragment.getSimpleCollectionPaths()) {
                QueryTree> newQP = split(
                    originalQT,
                    fragment,
                    null);
                newQueries.add(newQP);
                fragment.removeSimpleCollectionPath(simpleColl);
                newQP.getRoot().addSimpleCollectionPath(simpleColl);
            }
        }

        if(fragment.getParallelCollectionCount() > 1) {
            // Now identify if that parallel collection occurs across the outgoing edges
            // i.e., check the parallel collection count of the child fragments
            // in addition to the outgoing edge being a collection

            boolean encounteredCollection = false;
            for(IntraQuery outgoing: originalQT.getOutEdges(fragment)) {
                QueryFragment child = outgoing.getEnd();
                if(outgoing.getProperty().isMany() || child.getParallelCollectionCount() > 0) {
                    if(encounteredCollection) {
                        // Split at this edge
                        QueryTree newQT = split(originalQT, fragment, outgoing);
                        newQueries.add(newQT);
                    } else {
                        encounteredCollection = true;
                    }
                }
            }
        }

        return newQueries;
    }

    private QueryTree split(QueryTree> originalQT, QueryFragment fragment, IntraQuery splitAtEdge) {
        QueryFragment clone = originalQT.copyRoot(this.aggregateTree);
        QueryTree newQT = new QueryTree(clone.getEntityType(), originalQT.getView());
        newQT.addVertex(clone);

        // Split the original QueryTree
        if(splitAtEdge != null) {
            IntraQuery newEdge = new IntraQuery(splitAtEdge.getName(), clone, splitAtEdge.getEnd(), splitAtEdge.getProperty());
            originalQT.split(splitAtEdge, newEdge, newQT);
        }

        // The aggregateTree is now a forest
        aggregateTree.addVertex(newQT);

        return newQT;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy