/*********************************************************************************************************************** * * Copyright (C) 2010 by the Stratosphere project (http://stratosphere.eu) * * 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 eu.stratosphere.util.dag; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import eu.stratosphere.util.AbstractIterator; import eu.stratosphere.util.ConversionIterator; import eu.stratosphere.util.FilteringIterable; import eu.stratosphere.util.Predicate; /** * @author Arvid Heise */ public class Graph<Node> implements Iterable<Graph<Node>.NodePath> { private List<Node> startNodes = new ArrayList<Node>(); private ConnectionNavigator<Node> navigator; private ConnectionModifier<Node> modifier; private final RootPath root = new RootPath(); /** * Initializes Graph. */ public Graph(final ConnectionModifier<Node> modifier, final List<Node> startNodes) { this.startNodes = startNodes; this.navigator = this.modifier = modifier; } public Graph(final ConnectionModifier<Node> modifier, final Node... startNodes) { this(modifier, Arrays.asList(startNodes)); } @SuppressWarnings("rawtypes") @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (this.getClass() != obj.getClass()) return false; final Graph other = (Graph) obj; return this.startNodes.equals(other.startNodes); } public Iterable<Graph<Node>.NodePath> findAll(final Node toFind, final boolean equal) { final Predicate<Graph<Node>.NodePath> selector = equal ? new Predicate<Graph<Node>.NodePath>() { @Override public boolean isTrue(final Graph<Node>.NodePath param) { return param.equals(toFind); }; } : new Predicate<Graph<Node>.NodePath>() { @Override public boolean isTrue(final Graph<Node>.NodePath param) { return param.getNode() == toFind; }; }; return new FilteringIterable<Graph<Node>.NodePath>(this, selector); } /** * Returns the startNodes. * * @return the startNodes */ public List<Node> getStartNodes() { return this.startNodes; } public NodePath getPath(final Node startNode, final int... indizes) { int startIndex = -1; for (int index = 0; index < this.startNodes.size(); index++) if (this.startNodes.get(index) == startNode) { startIndex = index; break; } if (startIndex == -1) throw new IllegalArgumentException("unknown start node"); NodePath nodePath = new NodePath(this.root, startNode, startIndex); for (final int pathIndex : indizes) nodePath = nodePath.followConnection(pathIndex); return nodePath; } public Iterable<Graph<Node>.NodePath> findAll(final Predicate<Graph<Node>.NodePath> predicate) { return new FilteringIterable<Graph<Node>.NodePath>(this, predicate); } public Iterable<Graph<Node>.NodePath> findAllIncomings(final Node node) { return new FilteringIterable<Graph<Node>.NodePath>(this, new Predicate<Graph<Node>.NodePath>() { @Override public boolean isTrue(final Graph<Node>.NodePath param) { for (final Node outgoing : param.getOutgoings()) if (outgoing == node) return true; return false; }; }); } public Iterable<Graph<Node>.NodePath> findAllTypes(final Class<?> clazz) { return new FilteringIterable<Graph<Node>.NodePath>(this, new Predicate<Graph<Node>.NodePath>() { @Override public boolean isTrue(final Graph<Node>.NodePath param) { return clazz.isInstance(param); }; }); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + this.startNodes.hashCode(); return result; } /* * (non-Javadoc) * @see java.lang.Iterable#iterator() */ @Override public Iterator<Graph<Node>.NodePath> iterator() { return new GraphIterator(); } public void replace(final Node toReplace, final Node replacement, final boolean equal) { for (final NodePath replaceNode : this.findAll(toReplace, equal)) { final int index = replaceNode.getIndex(); final NodePath incoming = replaceNode.getIncoming(); this.replaceChild(incoming, index, replacement); } } public void replaceChild(final NodePath referencingNode, final int index, final Node replacement) { final List<Node> outgoings = referencingNode.getOutgoings(); outgoings.set(index, replacement); referencingNode.setOutgoings(outgoings); } /** * @author Arvid Heise */ public class GraphIterator extends AbstractIterator<NodePath> { private NodePath path = new StartPath(); private NodePath followNext(final NodePath path, final int nextIndex) { final NodePath incoming = path.getIncoming(); if (incoming == null) return this.noMoreElements(); if (nextIndex < incoming.getOutgoingCount()) return incoming.followConnection(nextIndex); return this.followNext(incoming, incoming.getIndex() + 1); } /* * (non-Javadoc) * @see eu.stratosphere.util.AbstractIterator#loadNext() */ @Override protected NodePath loadNext() { if (this.path.getOutgoingCount() > 0) return this.path = this.path.followConnection(0); return this.path = this.followNext(this.path, this.path.getIndex() + 1); } /* * (non-Javadoc) * @see eu.stratosphere.util.AbstractIterator#remove() */ @Override public void remove() { final NodePath referencingNode = this.path.getIncoming(); final List<Node> outgoings = referencingNode.getOutgoings(); outgoings.remove(this.path.getIndex()); referencingNode.setOutgoings(outgoings); } private class StartPath extends NodePath { /** * Initializes Graph.GraphIterator.StartPath. */ public StartPath() { super(Graph.this.root, null, -1); } /* * (non-Javadoc) * @see eu.stratosphere.util.dag.Graph.NodePath#getOutgoingCount() */ @Override public int getOutgoingCount() { return 0; } /* * (non-Javadoc) * @see eu.stratosphere.util.dag.Graph.NodePath#toString() */ @Override public String toString() { return "start"; } } } public class NodePath implements Iterable<NodePath> { private NodePath parentPath; private final Node node; private int index; private NodePath(final Node node) { this.node = node; } private NodePath(final NodePath parentPath, final Node node, final int followedIndex) { this.parentPath = parentPath; this.node = node; this.index = followedIndex; } public NodePath followConnection(final int connectionIndex) { return new NodePath(this, Graph.this.navigator.getConnectedNodes(this.node).get(connectionIndex), connectionIndex); } public Iterable<NodePath> getAllIncoming() { return this.parentPath; } public NodePath getIncoming() { return this.parentPath; } /** * Returns the followedIndex. * * @return the followedIndex */ public int getIndex() { return this.index; } /** * Returns the node. * * @return the node */ public Node getNode() { return this.node; } public int getOutgoingCount() { return Graph.this.navigator.getConnectedNodes(this.node).size(); } @SuppressWarnings("unchecked") public List<Node> getOutgoings() { return (List<Node>) Graph.this.navigator.getConnectedNodes(this.node); } /* * (non-Javadoc) * @see java.lang.Iterable#iterator() */ @Override public Iterator<NodePath> iterator() { return new ConversionIterator<Node, NodePath>(Graph.this.navigator.getConnectedNodes(this.node).iterator()) { int outIndex = 0; @Override protected NodePath convert(final Node inputObject) { return new NodePath(NodePath.this, inputObject, this.outIndex++); }; }; } public void setOutgoings(final List<Node> outgoings) { Graph.this.modifier.setConnectedNodes(this.node, outgoings); } /* * (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); if (this.parentPath != null) builder.append(this.parentPath.toString()).append("[").append(this.index).append("]=>"); builder.append(this.node.toString()); return builder.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + this.getOuterType().hashCode(); result = prime * result + this.index; result = prime * result + (this.node == null ? 0 : this.node.hashCode()); result = prime * result + (this.parentPath == null ? 0 : this.parentPath.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (this.getClass() != obj.getClass()) return false; @SuppressWarnings("unchecked") final NodePath other = (NodePath) obj; return this.getOuterType().equals(other.getOuterType()) && this.index == other.index && (this.node == null ? other.node == null : this.node.equals(other.node)) && (this.parentPath == null ? other.parentPath == null : this.parentPath.equals(other.parentPath)); } private Graph<Node> getOuterType() { return Graph.this; } } private class RootPath extends NodePath { /** * Initializes Graph.RootPath. */ public RootPath() { super(null); } /* * (non-Javadoc) * @see eu.stratosphere.util.dag.Graph.NodePath#followConnection(int) */ @Override public NodePath followConnection(final int connectionIndex) { return new NodePath(this, Graph.this.startNodes.get(connectionIndex), connectionIndex); } /* * (non-Javadoc) * @see eu.stratosphere.util.dag.Graph.NodePath#getOutgoingCount() */ @Override public int getOutgoingCount() { return Graph.this.startNodes.size(); } /* * (non-Javadoc) * @see eu.stratosphere.util.dag.Graph.NodePath#toString() */ @Override public String toString() { return "root"; } } }