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

com.espertech.esper.util.GraphUtil Maven / Gradle / Ivy

There is a newer version: 7.1.0
Show newest version
/*
 ***************************************************************************************
 *  Copyright (C) 2006 EsperTech, Inc. All rights reserved.                            *
 *  http://www.espertech.com/esper                                                     *
 *  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 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.
     * 

* 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. *

* 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 mergeNestableMap(Map original, Map additional) { Map result = new LinkedHashMap(original); for (Map.Entry 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 innerAdditional = (Map) additionalValue; Map innerOriginal = (Map) 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 getTopDownOrder(Map> graph) throws GraphCircularDependencyException { Stack circularDependency = getFirstCircularDependency(graph); if (circularDependency != null) { throw new GraphCircularDependencyException("Circular dependency detected between " + circularDependency); } Map> reversedGraph = new HashMap>(); // Reverse the graph - build a list of children per parent for (Map.Entry> entry : graph.entrySet()) { Set parents = entry.getValue(); String child = entry.getKey(); for (String parent : parents) { Set childList = reversedGraph.get(parent); if (childList == null) { childList = new LinkedHashSet(); reversedGraph.put(parent, childList); } childList.add(child); } } // Determine all root nodes, which are those without parent TreeSet roots = new TreeSet(); for (Set parents : graph.values()) { if (parents == null) { continue; } for (String parent : parents) { // 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 graphFlattened = new LinkedHashSet(); for (String root : roots) { recusiveAdd(graphFlattened, root, reversedGraph); } // now walk down the default order and for each node ensure all parents are created Set created = new LinkedHashSet(); Set removeList = new HashSet(); 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 created, Map> graph) { Set parents = graph.get(node); if (parents == null) { return true; } for (String parent : parents) { if (!created.contains(parent)) { return false; } boolean allParentsCreated = recursiveParentsCreated(parent, created, graph); if (!allParentsCreated) { return false; } } return true; } private static void recusiveAdd(Set graphFlattened, String root, Map> reversedGraph) { graphFlattened.add(root); Set 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 getFirstCircularDependency(Map> graph) { for (String child : graph.keySet()) { Stack deepDependencies = new Stack(); deepDependencies.push(child); boolean isCircular = recursiveDeepDepends(deepDependencies, child, graph); if (isCircular) { return deepDependencies; } } return null; } private static boolean recursiveDeepDepends(Stack deepDependencies, String currentChild, Map> graph) { Set required = graph.get(currentChild); if (required == null) { return false; } for (String parent : required) { if (deepDependencies.contains(parent)) { return true; } deepDependencies.push(parent); boolean isDeep = recursiveDeepDepends(deepDependencies, parent, graph); if (isDeep) { return true; } deepDependencies.pop(); } return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy