com.gs.obevo.impl.graph.GraphSorter.java Source code

Java tutorial

Introduction

Here is the source code for com.gs.obevo.impl.graph.GraphSorter.java

Source

/**
 * Copyright 2017 Goldman Sachs.
 * 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 com.gs.obevo.impl.graph;

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;

import org.apache.commons.collections.IteratorUtils;
import org.eclipse.collections.api.RichIterable;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.impl.factory.Lists;
import org.jgrapht.DirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DirectedSubgraph;
import org.jgrapht.traverse.TopologicalOrderIterator;

/**
 * Iterates through the inputs and graph to come up w/ a proper topological sorting.
 *
 * We expect that the graph elements should either be Comparable, or a Comparator be provided. This is to guarantee a
 * consistent topological order, which is much friendlier for clients to debug and for consistency across different
 * environments.
 *
 * (Note that on a given graph, we could have many valid topological orders, which is what we want to get consistency
 * on - see https://en.wikipedia.org/wiki/Topological_sorting).
 */
public class GraphSorter {
    /**
     * Sorts the graph to provide a consistent topological ordering. The vertices of the graph must implement {@link Comparable}
     *
     * @param graph          The input graph
     * @param subsetVertices The subset vertices of the graph we want to sort
     */
    public <T> ImmutableList<T> sortChanges(DirectedGraph<T, DefaultEdge> graph, RichIterable<T> subsetVertices) {
        return sortChanges(graph, subsetVertices, null);
    }

    /**
     * Sorts the graph to provide a consistent topological ordering.
     *
     * @param graph          The input graph
     * @param subsetVertices The subset vertices of the graph we want to sort
     * @param comparator     The comparator on which to order the vertices to guarantee a consistent topological ordering
     */
    public <T> ImmutableList<T> sortChanges(DirectedGraph<T, DefaultEdge> graph, RichIterable<T> subsetVertices,
            Comparator<? super T> comparator) {
        if (subsetVertices.toSet().size() != subsetVertices.size()) {
            throw new IllegalStateException("Unexpected state - have some dupe elements here: " + subsetVertices);
        }

        DirectedGraph<T, DefaultEdge> subsetGraph = new DirectedSubgraph<T, DefaultEdge>(graph,
                subsetVertices.toSet(), null);

        // At one point, we _thought_ that the DirectedSubGraph was dropping vertices that don't have edges, so we
        // manually add them back to the graph to ensure that we can still order them.
        // However, that no longer seems to be the case. We add a check here just in case this comes up again.
        if (subsetVertices.size() != subsetGraph.vertexSet().size()) {
            throw new IllegalArgumentException("This case should never happen! [subsetVertices: " + subsetVertices
                    + ", subsetGraphVertices: " + subsetGraph.vertexSet());
        }

        return sortChanges(subsetGraph, comparator);
    }

    /**
     * Sorts the graph to provide a consistent topological ordering. The vertices of the graph must implement {@link Comparable}
     *
     * @param graph The input graph - all vertices in the graph will be returned in the output list
     */
    public <T> ImmutableList<T> sortChanges(final DirectedGraph<T, DefaultEdge> graph) {
        return sortChanges(graph, (Comparator<T>) null);
    }

    /**
     * Sorts the graph to provide a consistent topological ordering.
     *
     * @param graph      The input graph - all vertices in the graph will be returned in the output list
     * @param comparator The comparator on which to order the vertices to guarantee a consistent topological ordering
     */
    public <T> ImmutableList<T> sortChanges(final DirectedGraph<T, DefaultEdge> graph,
            Comparator<? super T> comparator) {
        if (graph.vertexSet().isEmpty()) {
            return Lists.immutable.empty();
        }

        GraphUtil.validateNoCycles(graph);

        TopologicalOrderIterator<T, DefaultEdge> iterator = getTopologicalOrderIterator(graph, comparator);

        return Lists.immutable.withAll(IteratorUtils.toList(iterator));
    }

    private <T> TopologicalOrderIterator<T, DefaultEdge> getTopologicalOrderIterator(
            DirectedGraph<T, DefaultEdge> graph, Comparator<? super T> comparator) {
        if (comparator != null) {
            Queue<T> queue = new PriorityQueue<T>(10, comparator);
            return new TopologicalOrderIterator<T, DefaultEdge>(graph, queue);
        } else if (graph.vertexSet().iterator().next() instanceof Comparable) {
            Queue<T> queue = new PriorityQueue<T>();
            return new TopologicalOrderIterator<T, DefaultEdge>(graph, queue);
        } else {
            throw new IllegalArgumentException(
                    "Unsortable graph elements - either need to provide a Comparator or have Comparable vertices to guarantee a consistent topological order");
        }
    }
}