com.clarkparsia.sbol.order.PartialOrder.java Source code

Java tutorial

Introduction

Here is the source code for com.clarkparsia.sbol.order.PartialOrder.java

Source

/*
 * Copyright (c) 2012 - 2015, Clark & Parsia, LLC. <http://www.clarkparsia.com>
 *
 * 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.clarkparsia.sbol.order;

import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multiset;
import com.google.common.collect.Sets;

/**
 * A collection of partially ordered elements. One or more elements can be added to this class and {@link #iterator()}
 * will return the elements in a ascending topological order.   
 * 
 * @author Evren Sirin
 */
public class PartialOrder<T> implements Iterable<T> {
    private final Map<T, Set<T>> precededBy = Maps.newLinkedHashMap();
    private final PartialOrderComparator<T> comparator;

    public PartialOrder(PartialOrderComparator<T> comparator) {
        this.comparator = comparator;
    }

    public PartialOrder(Iterable<T> elements, PartialOrderComparator<T> comparator) {
        this.comparator = comparator;
        addAll(elements);
    }

    public boolean addAll(Iterable<T> newElements) {
        boolean added = false;
        for (T newElement : newElements) {
            added |= add(newElement);
        }
        return added;
    }

    public boolean add(T newElement) {
        if (precededBy.containsKey(newElement)) {
            return false;
        }

        Set<T> precededByList = Sets.newHashSet();
        List<T> precedesList = Lists.newArrayList();
        for (T e : precededBy.keySet()) {
            PartialOrderRelation cmp = comparator.compare(newElement, e);
            if (cmp == PartialOrderRelation.LESS) {
                precedesList.add(e);
            } else if (cmp == PartialOrderRelation.GREATER) {
                precededByList.add(e);
            }
        }

        for (T e : precedesList) {
            precededBy.get(e).add(newElement);
        }

        precededBy.put(newElement, precededByList);

        return true;
    }

    /**
     * Returns the elements in an ascending topological order.
     * 
     * @throws IllegalStateException if there are cycles between the elements
     */
    @Override
    public Iterator<T> iterator() throws IllegalStateException {
        Multiset<T> degrees = HashMultiset.create();
        Queue<T> nodesPending = new ArrayDeque<T>();
        List<T> nodesSorted = Lists.newArrayList();

        for (Entry<T, Set<T>> entry : precededBy.entrySet()) {
            T node = entry.getKey();
            Set<T> precededByList = entry.getValue();
            int degree = precededByList.size();
            degrees.setCount(node, degree);
            if (degree == 0) {
                nodesPending.add(node);
            }
        }

        while (!nodesPending.isEmpty()) {
            T node = nodesPending.remove();

            int deg = degrees.count(node);
            if (deg != 0)
                throw new IllegalStateException("Cycle detected " + node + " " + deg + " " + nodesSorted.size());

            nodesSorted.add(node);

            for (Entry<T, Set<T>> entry : precededBy.entrySet()) {
                T n = entry.getKey();
                Set<T> precededByList = entry.getValue();
                if (precededByList.contains(node)) {
                    int degree = degrees.count(n);
                    if (degree == 1) {
                        nodesPending.add(n);
                        degrees.setCount(n, 0);
                    } else {
                        degrees.remove(n);
                    }
                }
            }
        }

        if (nodesSorted.size() != precededBy.size()) {
            throw new IllegalStateException("Failed to sort elements");
        }

        return nodesSorted.iterator();
    }

    public int size() {
        return precededBy.size();
    }
}