org.eclipse.emf.compare.ide.ui.internal.logical.Graph.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.compare.ide.ui.internal.logical.Graph.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Obeo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.emf.compare.ide.ui.internal.logical;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterators.concat;

import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import org.eclipse.emf.common.util.AbstractTreeIterator;

/**
 * This structure will be used to maintain a undirected graph of elements.
 * <p>
 * This boils down to maintaining a list of children and parents of each node, updating them in sync with each
 * other.
 * </p>
 * <p>
 * Take note that the elements of this graph are not necessarily all connected together. This can be used to
 * represent a set of trees, a set of undirected graphs, a set of roots with no children...
 * </p>
 * 
 * @param <E>
 *            Kind of elements used as this graph's nodes.
 * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
 */
public final class Graph<E> {
    /** Keeps track of this graph's individual nodes. */
    private final Map<E, Node<E>> nodes;

    /** Constructs an empty graph. */
    public Graph() {
        this.nodes = new LinkedHashMap<E, Node<E>>();
    }

    /**
     * Checks whether this graph already contains the given element.
     * 
     * @param element
     *            Element we need to check.
     * @return <code>true</code> if this graph already contains the given elment, <code>false</code>
     *         otherwise.
     */
    public boolean contains(E element) {
        synchronized (nodes) {
            return nodes.containsKey(element);
        }
    }

    /**
     * Adds a new element to this graph, if it does not exists yet. Elements will initially have no connection
     * to other elements, and can thus be considered "roots" of the graph.
     * 
     * @param element
     *            The element to add as a new root to this graph.
     * @return <code>true</code> if this element did not previously exist in the graph.
     */
    public boolean add(E element) {
        synchronized (nodes) {
            Node<E> node = nodes.get(element);
            if (node == null) {
                node = new Node<E>(element);
                nodes.put(element, node);
                return true;
            }
            return false;
        }
    }

    /**
     * Removes the given element's node from this graph. This will effectively break all connections to that
     * node.
     * 
     * @param element
     *            The element which is to be removed from this graph.
     */
    public void remove(E element) {
        synchronized (nodes) {
            final Node<E> node = nodes.remove(element);
            if (node != null) {
                node.breakConnections();
            }
        }
    }

    /**
     * Connects the given set of elements to a given parent. Note that nodes will be created for all new
     * elements if they do not exist yet.
     * 
     * @param element
     *            The element that is to be connected with new children.
     * @param newChildren
     *            The set of elements to connect to the given parent.
     */
    public void addChildren(E element, Set<E> newChildren) {
        synchronized (nodes) {
            Node<E> node = nodes.get(element);
            if (node == null) {
                node = new Node<E>(element);
                nodes.put(element, node);
            }

            for (E newChild : newChildren) {
                Node<E> childNode = nodes.get(newChild);
                if (childNode == null) {
                    childNode = new Node<E>(newChild);
                    nodes.put(newChild, childNode);
                }
                node.connectChild(childNode);
            }
        }
    }

    /**
     * Checks if the given element is a parent of the given potential child, directly or not.
     * 
     * @param parent
     *            Element that could be a parent of <code>potentialChild</code>.
     * @param potentialChild
     *            The potential child of <code>parent</code>.
     */
    public boolean hasChild(E parent, E potentialChild) {
        synchronized (nodes) {
            final Node<E> node = nodes.get(potentialChild);
            if (node != null) {
                return Iterables.any(node.getAllParents(), is(parent));
            }
            return false;
        }
    }

    /**
     * This predicate will be used to check if a Node's element is the given one.
     * 
     * @return The constructed predicate.
     */
    private Predicate<? super Node<E>> is(final E element) {
        return new Predicate<Node<E>>() {
            public boolean apply(Node<E> input) {
                return input != null && input.getElement() == element;
            }
        };
    }

    /**
     * Returns an iterable over all elements of the subgraph containing the given element.
     * 
     * @param element
     *            Element we need the subgraph of.
     * @return An iterable over all elements of the subgraph containing the given element, an empty list if
     *         that element is not present in this graph.
     */
    public Iterable<E> getSubgraphOf(E element) {
        return getBoundedSubgraphOf(element, Collections.<E>emptySet());
    }

    /**
     * Returns an iterable over all elements of the subgraph containing the given element and ending at the
     * given boundaries.
     * 
     * @param element
     *            Element we need the subgraph of.
     * @param endPoints
     *            Boundaries of the need subgraph.
     * @return An iterable over all elements of the subgraph containing the given element, an empty list if
     *         that element is not present in this graph.
     */
    public Iterable<E> getBoundedSubgraphOf(E element, Set<E> endPoints) {
        synchronized (nodes) {
            final Node<E> node = nodes.get(element);
            if (node != null) {
                Set<E> boundaries = endPoints;
                if (boundaries == null) {
                    boundaries = Collections.emptySet();
                }
                return new SubgraphBuilder<E>(node, boundaries).build();
            }
            return Collections.emptyList();
        }
    }

    /**
     * This will be used to represent individual elements in our graph.
     * 
     * @param <K>
     *            Type of the Node's underlying data.
     * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
     */
    private static class Node<K> {
        /** Underlying data of this Node. */
        private final K element;

        /** Nodes that are connected with this one as a parent. */
        private final Set<Node<K>> children;

        /** Nodes that are connected with this one as a child. */
        private final Set<Node<K>> parents;

        /**
         * Construct a new Node for the given element.
         * 
         * @param element
         *            The element for which we need a graph Node.
         */
        public Node(K element) {
            this.element = element;
            this.parents = new LinkedHashSet<Node<K>>();
            this.children = new LinkedHashSet<Node<K>>();
        }

        /**
         * Returns all children nodes of this one.
         * 
         * @return All children nodes of this one.
         */
        public Set<Node<K>> getChildren() {
            return Collections.unmodifiableSet(children);
        }

        /**
         * Returns all parent nodes of this one.
         * 
         * @return All parent nodes of this one.
         */
        public Set<Node<K>> getParents() {
            return Collections.unmodifiableSet(parents);
        }

        /**
         * Returns all direct and indirect parents of this node.
         * 
         * @return All direct and indirect parents of this node.
         */
        public Iterable<Node<K>> getAllParents() {
            return new ParentsIterable<K>(this);
        }

        /**
         * Registers the given element as a child of this one.
         * 
         * @param other
         *            The Node that is to be connected to this one.
         */
        public void connectChild(Node<K> child) {
            this.children.add(child);
            child.parents.add(this);
        }

        /**
         * Breaks all connections of this node.
         */
        public void breakConnections() {
            for (Node<K> parent : this.parents) {
                parent.children.remove(this);
            }
            for (Node<K> child : this.children) {
                child.parents.remove(this);
            }
            this.parents.clear();
            this.children.clear();
        }

        /**
         * Returns the underlying data of this Node.
         * 
         * @return The underlying data of this Node.
         */
        public K getElement() {
            return element;
        }
    }

    /**
     * This will be used to create a set over an entire subgraph given a starting point in said subgraph.
     * 
     * @param <L>
     *            Kind of elements the created set is meant to contain.
     * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
     */
    private static class SubgraphBuilder<L> {
        /** The Node that will be used as a starting point of the iteration. */
        private final Node<L> start;

        /** Keeps track of the elements we've already iterated over. */
        protected final Set<L> set;

        /** Nodes that will be considered as boundaries for this subgraph. */
        protected final Set<L> endPoints;

        /**
         * Constructs a new iterable given the starting point the target subgraph and the elements that will
         * be considered as (excluded) boundaries of that subgraph.
         * 
         * @param start
         *            Starting point of the iteration.
         * @param endPoints
         *            Excluded boundaries of the target subgraph. Iteration will be pruned on these, along
         *            with their own subgraphs.
         */
        public SubgraphBuilder(Node<L> start, Set<L> endPoints) {
            this.start = start;
            this.set = new LinkedHashSet<L>();
            this.set.add(start.getElement());
            this.endPoints = checkNotNull(endPoints);
        }

        /**
         * Constructs the set corresponding to the required subgraph.
         * 
         * @return The set of all nodes composing the required subgraph.
         */
        public Set<L> build() {
            return ImmutableSet.copyOf(new NodeIterator(start));
        }

        /**
         * A node iterator will allow us to iterate over the data of a Node and the data of all directly
         * connected Nodes; all the while avoiding data we've already iterated over.
         * <p>
         * This kind of iterator does not support {@link #remove() removal}.
         * </p>
         * <p>
         * Note : this is not a static class as we're building the external SubgraphBuilder's set through this
         * (and checking for already iterated over elements).
         * </p>
         * 
         * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
         */
        private class NodeIterator implements Iterator<L> {
            /** Iterator over the all nodes directly connected with the underlying Node. */
            private final Iterator<Node<L>> nodesIterator;

            /**
             * Keeps track of the next Node's iterator. This will allow recursion over the connected Nodes'
             * own connections.
             */
            private Iterator<L> nextNodeIterator;

            /** Element that should be returned by the next call to {@link #next()}. */
            private L next;

            /**
             * Construct an iterator over the given Node's connected data.
             * 
             * @param node
             *            The node for which we need an iterator.
             */
            public NodeIterator(Node<L> node) {
                this.next = node.getElement();
                this.nodesIterator = concat(node.getParents().iterator(), node.getChildren().iterator());
                prepareNextIterator();
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.util.Iterator#hasNext()
             */
            public boolean hasNext() {
                return next != null || nextNodeIterator.hasNext() || nodesIterator.hasNext();
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.util.Iterator#next()
             */
            public L next() {
                if (next == null) {
                    throw new NoSuchElementException();
                }
                L result = next;
                prepareNext();
                return result;
            }

            /**
             * {@inheritDoc}
             * 
             * @see java.util.Iterator#remove()
             */
            public void remove() {
                throw new UnsupportedOperationException();
            }

            /**
             * This will be called to prepare for a subsequent call to {@link #next()}.
             */
            private void prepareNext() {
                if (!nextNodeIterator.hasNext()) {
                    prepareNextIterator();
                }
                if (nextNodeIterator.hasNext()) {
                    next = nextNodeIterator.next();
                } else {
                    next = null;
                }
            }

            /**
             * This will be called whenever we've consumed all nodes connected to the current to "jump over"
             * to the next one.
             */
            private void prepareNextIterator() {
                if (nodesIterator.hasNext()) {
                    Node<L> nextNode = nodesIterator.next();
                    while (SubgraphBuilder.this.set.contains(nextNode.getElement())
                            && !SubgraphBuilder.this.endPoints.contains(nextNode.getElement())
                            && nodesIterator.hasNext()) {
                        nextNode = nodesIterator.next();
                    }
                    if (!SubgraphBuilder.this.endPoints.contains(nextNode.getElement())
                            && SubgraphBuilder.this.set.add(nextNode.getElement())) {
                        nextNodeIterator = new NodeIterator(nextNode);
                    } else {
                        nextNodeIterator = Iterators.emptyIterator();
                    }
                } else {
                    nextNodeIterator = Iterators.emptyIterator();
                }
            }
        }
    }

    /**
     * A custom Iterable that will iterate over the Node->parent Node tree of a given Node.
     * 
     * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
     */
    private static class ParentsIterable<M> implements Iterable<Node<M>> {
        /** The leaf of the Node->parent tree for which this iterable has been constructed. */
        private final Node<M> start;

        /**
         * Constructs an iterable given the root of its tree
         * 
         * @param start
         */
        public ParentsIterable(Node<M> start) {
            this.start = start;
        }

        /**
         * {@inheritDoc}
         * 
         * @see java.lang.Iterable#iterator()
         */
        public Iterator<Node<M>> iterator() {
            return new ParentsIterator<M>(start);
        }
    }

    /**
     * A custom TreeIterator that will iterate over the Node->parent Node tree.
     * 
     * @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
     */
    private static class ParentsIterator<N> extends AbstractTreeIterator<Node<N>> {
        /** Generated SUID. */
        private static final long serialVersionUID = -4476850344598138970L;

        /**
         * Construct an iterator given the root (well, "leaf") of its tree.
         * 
         * @param start
         *            Start node of the tree we'll iterate over.
         */
        public ParentsIterator(Node<N> start) {
            super(start, false);
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.emf.common.util.AbstractTreeIterator#getChildren(java.lang.Object)
         */
        @SuppressWarnings("unchecked")
        @Override
        protected Iterator<? extends Node<N>> getChildren(Object obj) {
            return ((Node<N>) obj).getParents().iterator();
        }
    }
}