Package com.espertech.esper.util

Source Code of com.espertech.esper.util.GraphUtil

/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved.                            *
* http://esper.codehaus.org                                                          *
* http://www.espertech.com                                                           *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license       *
* a copy of which has been included with this distribution in the license.txt file.  *
**************************************************************************************/
package com.espertech.esper.util;

import com.espertech.esper.client.ConfigurationEventTypeMap;

import java.util.*;

/**
* Utility for working with acyclic graph: determines cyclic dependency and dependency-satisfying processing order.
*/
public class GraphUtil
{
    /**
     * Deep-merge a map into another map returning a result map.
     * <p>
     * Copies all values present in the original map to a new map,
     * adding additional value present in the second map passed in,
     * ignoring same-key values in the second map that are present in the original.
     * <p>
     * If the value is a Map itself, repeats the operation on the Map value.
     * @param original nestable Map of entries to retain and not overwrite
     * @param additional nestable Map of entries to add to the original
     * @return merge of original and additional nestable map
     */
    public static Map<String, Object> mergeNestableMap(Map<String, Object> original, Map<String, Object> additional)
    {
        Map<String, Object> result = new HashMap<String, Object>(original);

        for (Map.Entry<String, Object> additionalEntry : additional.entrySet())
        {
            String name = additionalEntry.getKey();
            Object additionalValue = additionalEntry.getValue();

            Object originalValue = original.get(name);

            Object newValue;
            if ((originalValue instanceof Map) &&
                (additionalValue instanceof Map))
            {
                Map<String, Object> innerAdditional = (Map<String, Object>) additionalValue;
                Map<String, Object> innerOriginal = (Map<String, Object>) originalValue;
                newValue = mergeNestableMap(innerOriginal, innerAdditional);
                result.put(name, newValue);
                continue;
            }

            if (original.containsKey(name))
            {
                continue;
            }
            result.put(name, additionalValue);
        }
        return result;
    }

    /**
     * Check cyclic dependency and determine processing order for the given graph.
     * @param graph is represented as child nodes that have one or more parent nodes that they are dependent on
     * @return set of parent and child nodes in order such that no node's dependency is not satisfied
     * by a prior nodein the set
     * @throws GraphCircularDependencyException if a dependency has been detected
     */
    public static Set<String> getTopDownOrder(Map<String, ConfigurationEventTypeMap> graph) throws GraphCircularDependencyException
    {
        Stack<String> circularDependency = getFirstCircularDependency(graph);
        if (circularDependency != null)
        {
            throw new GraphCircularDependencyException("Circular dependency detected between " + circularDependency);
        }

        Map<String, Set<String>> reversedGraph = new HashMap<String, Set<String>>();

        // Reverse the graph - build a list of children per parent
        for (Map.Entry<String, ConfigurationEventTypeMap> entry : graph.entrySet())
        {
            Set<String> parents = entry.getValue().getSuperTypes();
            String child = entry.getKey();

            for (String parent : parents)
            {
                Set<String> childList = reversedGraph.get(parent);
                if (childList == null)
                {
                    childList = new LinkedHashSet<String>();
                    reversedGraph.put(parent, childList);
                }
                childList.add(child);
            }
        }

        // Determine all root nodes, which are those without parent
        TreeSet<String> roots = new TreeSet<String>();
        for (ConfigurationEventTypeMap parents : graph.values())
        {
            if (parents == null)
            {
                continue;
            }
            for (String parent : parents.getSuperTypes())
            {
                // node not itself a child
                if (!graph.containsKey(parent))
                {
                    roots.add(parent);
                }
            }
        }

        // for each root, recursively add its child nodes, this becomes the default order
        Set<String> graphFlattened = new LinkedHashSet<String>();
        for (String root : roots)
        {
            recusiveAdd(graphFlattened, root, reversedGraph);
        }

        // now walk down the default order and for each node ensure all parents are created
        Set<String> created = new LinkedHashSet<String>();
        Set<String> removeList = new HashSet<String>();
        while (!graphFlattened.isEmpty())
        {
            removeList.clear();
            for (String node : graphFlattened)
            {
                if (!recursiveParentsCreated(node, created, graph))
                {
                    continue;
                }
                created.add(node);
                removeList.add(node);
            }
            graphFlattened.removeAll(removeList);
        }

        return created;
    }

    // Determine if all the node's parents and their parents have been added to the created set
    private static boolean recursiveParentsCreated(String node, Set<String> created, Map<String, ConfigurationEventTypeMap> graph)
    {
        ConfigurationEventTypeMap parents = graph.get(node);
        if (parents == null)
        {
            return true;
        }
        for (String parent : parents.getSuperTypes())
        {
            if (!created.contains(parent))
            {
                return false;
            }
            boolean allParentsCreated = recursiveParentsCreated(parent, created, graph);
            if (!allParentsCreated)
            {
                return false;
            }
        }
        return true;
    }

    private static void recusiveAdd(Set<String> graphFlattened, String root, Map<String, Set<String>> reversedGraph)
    {
        graphFlattened.add(root);
        Set<String> childNodes = reversedGraph.get(root);
        if (childNodes == null)
        {
            return;
        }
        for (String child : childNodes)
        {
            recusiveAdd(graphFlattened, child, reversedGraph);
        }
    }

    /**
     * Returns any circular dependency as a stack of stream numbers, or null if none exist.
     * @param graph the dependency graph
     * @return circular dependency stack
     */
    private static Stack<String> getFirstCircularDependency(Map<String, ConfigurationEventTypeMap> graph)
    {
        for (String child : graph.keySet())
        {
            Stack<String> deepDependencies = new Stack<String>();
            deepDependencies.push(child);

            boolean isCircular = recursiveDeepDepends(deepDependencies, child, graph);
            if (isCircular)
            {
                return deepDependencies;
            }
        }
        return null;
    }

    private static boolean recursiveDeepDepends(Stack<String> deepDependencies, String currentChild, Map<String, ConfigurationEventTypeMap> graph)
    {
        ConfigurationEventTypeMap required = graph.get(currentChild);
        if (required == null)
        {
            return false;
        }

        for (String parent : required.getSuperTypes())
        {
            if (deepDependencies.contains(parent))
            {
                return true;
            }
            deepDependencies.push(parent);
            boolean isDeep = recursiveDeepDepends(deepDependencies, parent, graph);
            if (isDeep)
            {
                return true;
            }
            deepDependencies.pop();
        }

        return false;
    }
}
TOP

Related Classes of com.espertech.esper.util.GraphUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.