/******************************************************************************* * Copyright (c) 2006, 2008 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.texteditor.rulers; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.Assert; /** * A directed acyclic graph. See http://en.wikipedia.org/wiki/Directed_acyclic_graph * * @since 3.3 */ public final class DAG { /** * Multimap, supports <code>null</code> key, but not <code>null</code> values. */ private static final class MultiMap { private final Map fMap= new LinkedHashMap(); /** * Adds <code>val</code> to the values mapped to by <code>key</code>. If * <code>val</code> is <code>null</code>, <code>key</code> is added to the key set of * the multimap. * * @param key the key * @param val the value */ public void put(Object key, Object val) { Set values= (Set) fMap.get(key); if (values == null) { values= new LinkedHashSet(); fMap.put(key, values); } if (val != null) values.add(val); } /** * Returns all mappings for the given key, an empty set if there are no mappings. * * @param key the key * @return the mappings for <code>key</code> */ public Set get(Object key) { Set values= (Set) fMap.get(key); return values == null ? Collections.EMPTY_SET : values; } public Set keySet() { return fMap.keySet(); } /** * Removes all mappings for <code>key</code> and removes <code>key</code> from the key * set. * * @param key the key to remove * @return the removed mappings */ public Set removeAll(Object key) { Set values= (Set) fMap.remove(key); return values == null ? Collections.EMPTY_SET : values; } /** * Removes a mapping from the multimap, but does not remove the <code>key</code> from the * key set. * * @param key the key * @param val the value */ public void remove(Object key, Object val) { Set values= (Set) fMap.get(key); if (values != null) values.remove(val); } /* * @see java.lang.Object#toString() */ public String toString() { return fMap.toString(); } } private final MultiMap fOut= new MultiMap(); private final MultiMap fIn= new MultiMap(); /** * Adds a directed edge from <code>origin</code> to <code>target</code>. The vertices are not * required to exist prior to this call - if they are not currently contained by the graph, they are * automatically added. * * @param origin the origin vertex of the dependency * @param target the target vertex of the dependency * @return <code>true</code> if the edge was added, <code>false</code> if the * edge was not added because it would have violated the acyclic nature of the * receiver. */ public boolean addEdge(Object origin, Object target) { Assert.isLegal(origin != null); Assert.isLegal(target != null); if (hasPath(target, origin)) return false; fOut.put(origin, target); fOut.put(target, null); fIn.put(target, origin); fIn.put(origin, null); return true; } /** * Adds a vertex to the graph. If the vertex does not exist prior to this call, it is added with * no incoming or outgoing edges. Nothing happens if the vertex already exists. * * @param vertex the new vertex */ public void addVertex(Object vertex) { Assert.isLegal(vertex != null); fOut.put(vertex, null); fIn.put(vertex, null); } /** * Removes a vertex and all its edges from the graph. * * @param vertex the vertex to remove */ public void removeVertex(Object vertex) { Set targets= fOut.removeAll(vertex); for (Iterator it= targets.iterator(); it.hasNext();) fIn.remove(it.next(), vertex); Set origins= fIn.removeAll(vertex); for (Iterator it= origins.iterator(); it.hasNext();) fOut.remove(it.next(), vertex); } /** * Returns the sources of the receiver. A source is a vertex with no incoming edges. The * returned set's iterator traverses the nodes in the order they were added to the graph. * * @return the sources of the receiver */ public Set getSources() { return computeZeroEdgeVertices(fIn); } /** * Returns the sinks of the receiver. A sink is a vertex with no outgoing edges. The returned * set's iterator traverses the nodes in the order they were added to the graph. * * @return the sinks of the receiver */ public Set getSinks() { return computeZeroEdgeVertices(fOut); } private Set computeZeroEdgeVertices(MultiMap map) { Set candidates= map.keySet(); Set roots= new LinkedHashSet(candidates.size()); for (Iterator it= candidates.iterator(); it.hasNext();) { Object candidate= it.next(); if (map.get(candidate).isEmpty()) roots.add(candidate); } return roots; } /** * Returns the direct children of a vertex. The returned {@link Set} is unmodifiable. * * @param vertex the parent vertex * @return the direct children of <code>vertex</code> */ public Set getChildren(Object vertex) { return Collections.unmodifiableSet(fOut.get(vertex)); } private boolean hasPath(Object start, Object end) { // break condition if (start == end) return true; Set children= fOut.get(start); for (Iterator it= children.iterator(); it.hasNext();) // recursion if (hasPath(it.next(), end)) return true; return false; } /* * @see java.lang.Object#toString() * @since 3.3 */ public String toString() { return "Out: " + fOut.toString() + " In: " + fIn.toString(); //$NON-NLS-1$ //$NON-NLS-2$ } }