/*******************************************************************************
* Copyright (c) 2007, 2011 Borland Software 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:
* Borland Software Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.evaluator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
public class GraphWalker {
public static interface NodeProvider {
Object[] getLinkedNodes(Object node);
}
public static interface VertexProcessor {
boolean process(Object node);
}
public static final int MAX_WEIGHT = Integer.MAX_VALUE;
public GraphWalker(NodeProvider nodeProvider) {
myNodeProvider = nodeProvider;
}
public boolean walkBreadthFirst(Object root, VertexProcessor processor) {
Set<Object> visited = new HashSet<Object>();
List<Object> queue = new ArrayList<Object>();
visited.add(root);
queue.add(root);
while (!queue.isEmpty()) {
Object u = queue.remove(0);
Object[] succ = myNodeProvider.getLinkedNodes(u);
for (int i = 0; i < succ.length; i++) {
Object v = succ[i];
if (!visited.contains(v)) {
queue.add(v);
visited.add(v);
}
}
boolean stop = processor.process(u);
if (stop) {
return true;
}
}
return false;
}
public static class CycleException extends RuntimeException {
private static final long serialVersionUID = -7749663546813761182L;
public CycleException(Object from, Object to) {
myFrom = from;
myTo = to;
}
public Object getFrom() { return myFrom; }
public Object getTo() { return myTo; }
@Override
public String toString() {
return "Cycle from " + myFrom + " to " + myTo; //$NON-NLS-1$ //$NON-NLS-2$
}
private final Object myFrom;
private final Object myTo;
}
public boolean walkDepthFirst(Object root, VertexProcessor processor) throws CycleException{
class Traverser {
Integer GREY = new Integer(1);
Integer BLACK = new Integer(2);
boolean walkDepthFirst(Object u, Map<Object, Integer> visited, VertexProcessor processor) {
visited.put(u, GREY);
Object[] succ = myNodeProvider.getLinkedNodes(u);
for (int i = 0; i < succ.length; i++) {
Object v = succ[i];
if (!visited.containsKey(v)) {
boolean stop = walkDepthFirst(v, visited, processor);
if (stop) {
return true;
}
}
else if (GREY == visited.get(v)){
throw new CycleException(u, v);
}
}
boolean stop = processor.process(u);
visited.put(u, BLACK);
return stop;
}
};
Map<Object, Integer> visited = new HashMap<Object, Integer>();
return new Traverser().walkDepthFirst(root, visited, processor);
}
public int getMinimumDistance(Object source, Object dest, int weight) {
Map<ObjectPair, Integer> pathToWeight = new HashMap<ObjectPair, Integer>();
getMinDistanceImpl(source, pathToWeight, weight);
Integer d = pathToWeight.get(new ObjectPair(source, dest));
return d == null ? MAX_WEIGHT : d.intValue();
}
private void getMinDistanceImpl(Object s, Map<ObjectPair, Integer> path2Weight, int weight) {
Object[] nodes = myNodeProvider.getLinkedNodes(s);
for (int k = 0; k < nodes.length; k++) {
Object t = nodes[k];
ObjectPair s2t = new ObjectPair(s, t);
if (path2Weight.get(s2t) == null || ((Integer)path2Weight.get(s2t)).intValue() > weight) {
path2Weight.put(s2t, new Integer(weight));
}
ObjectPair t2t = new ObjectPair(t, t);
if (path2Weight.get(t2t) == null) {
getMinDistanceImpl(t, path2Weight, weight);
}
Set<Map.Entry<ObjectPair, Integer>> paths = new HashSet<Map.Entry<ObjectPair, Integer>>(path2Weight.entrySet());
for (Iterator<Entry<ObjectPair, Integer>> entryIt = paths.iterator(); entryIt.hasNext(); ) {
Map.Entry<ObjectPair, Integer> entry = entryIt.next();
ObjectPair t2i = (ObjectPair)entry.getKey();
if(!t.equals(t2i.getFirst())) {
continue;
}
Integer t2iW = (Integer)entry.getValue();
Object i = t2i.getSecond();
ObjectPair s2i = new ObjectPair(s, i);
if (path2Weight.get(s2i) == null || ((Integer)path2Weight.get(s2i)).intValue() > weight + t2iW.intValue()) {
path2Weight.put(s2i, new Integer(weight+t2iW.intValue()));
}
}
}
ObjectPair s2s = new ObjectPair(s, s);
path2Weight.put(s2s, new Integer(0));
}
private static class ObjectPair {
public ObjectPair(Object first, Object second) {
myFirst = first;
mySecond = second;
}
public Object getFirst() {
return myFirst;
}
public Object getSecond() {
return mySecond;
}
@Override
public boolean equals(Object o) {
if (o instanceof ObjectPair == false) {
return false;
}
ObjectPair pair = (ObjectPair)o;
return myFirst.equals(pair.myFirst) && mySecond.equals(pair.mySecond);
}
@Override
public int hashCode() {
return 17 + myFirst.hashCode()*37 + mySecond.hashCode();
}
private final Object myFirst;
private final Object mySecond;
}
private final NodeProvider myNodeProvider;
}