/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (props, at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package org.opentripplanner.routing.core;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.Vertex;
/**
* Allows adding supplemental edges to existing vertices in another graph. Useful for CH, and
* potentially for "extraEdges".
*
* @author andrewbyrd
*/
public class OverlayGraph implements Serializable {
private static final long serialVersionUID = 20111106L; // YYYYMMDD
private static final int INITIAL_EDGELIST_CAPACITY = 5;
private IdentityHashMap<Vertex, List<Edge>> outgoing;
private IdentityHashMap<Vertex, List<Edge>> incoming;
/**
* Create an empty OverlayGraph.
*/
public OverlayGraph() {
outgoing = new IdentityHashMap<Vertex, List<Edge>>();
incoming = new IdentityHashMap<Vertex, List<Edge>>();
}
/**
* Copy contents of a Graph into this OverlayGraph
*/
public OverlayGraph(Graph g) {
this();
for (Vertex v : g.getVertices()) {
for (Edge e : v.getOutgoing())
this.addOutgoing(v, e);
for (Edge e : v.getIncoming())
this.addIncoming(v, e);
}
}
public void addOutgoing(Vertex fromv, Edge e) {
List<Edge> fromOutgoing = outgoing.get(fromv);
if (fromOutgoing == null) {
fromOutgoing = new ArrayList<Edge>(INITIAL_EDGELIST_CAPACITY);
outgoing.put(fromv, fromOutgoing);
}
if (!fromOutgoing.contains(e))
fromOutgoing.add(e);
}
public void addIncoming(Vertex tov, Edge e) {
List<Edge> toIncoming = incoming.get(tov);
if (toIncoming == null) {
toIncoming = new ArrayList<Edge>(INITIAL_EDGELIST_CAPACITY);
incoming.put(tov, toIncoming);
}
if (!toIncoming.contains(e))
toIncoming.add(e);
}
public void removeOutgoing(Vertex fromv, Edge e) {
List<Edge> fromOutgoing = outgoing.get(fromv);
if (fromOutgoing != null) {
fromOutgoing.remove(e);
}
}
public void removeIncoming(Vertex tov, Edge e) {
List<Edge> toIncoming = incoming.get(tov);
if (toIncoming != null) {
toIncoming.remove(e);
}
}
public void addEdge(Edge e) {
Vertex fromv = e.getFromVertex();
Vertex tov = e.getToVertex();
addOutgoing(fromv, e);
addIncoming(tov, e);
}
public void removeEdge(Edge e) {
Vertex fromv = e.getFromVertex();
Vertex tov = e.getToVertex();
removeOutgoing(fromv, e);
removeIncoming(tov, e);
}
public List<Edge> getOutgoing(Vertex v) {
List<Edge> ret = outgoing.get(v);
if (ret == null)
ret = Collections.emptyList();
return ret;
}
public List<Edge> getIncoming(Vertex v) {
List<Edge> ret = incoming.get(v);
if (ret == null)
ret = Collections.emptyList();
return ret;
}
/**
* A single edge can appear once or twice. (CH graphs might have only outgoing or only incoming
* edges.) Avoid double-counting.
*/
public int countEdges() {
HashSet<Edge> eset = new HashSet<Edge>(1000);
for (List<Edge> l : outgoing.values())
for (Edge e : l)
eset.add(e);
for (List<Edge> l : incoming.values())
for (Edge e : l)
eset.add(e);
return eset.size();
}
/**
* A single Vertex can appear once or twice. (CH graphs might have only outgoing or only
* incoming edges.) Avoid double-counting.
*/
public Collection<Vertex> getVertices() {
HashSet<Vertex> sv = new HashSet<Vertex>();
sv.addAll(outgoing.keySet());
sv.addAll(incoming.keySet());
return sv;
}
/**
* A single Vertex can appear once or twice. (CH graphs might have only outgoing or only
* incoming edges.) Avoid double-counting. This is very inefficient.
*/
public int countVertices() {
return getVertices().size();
}
// need to make sure lists are never null - Vertex
// beware concurrentModification of lists.
// public void removeVertex(Vertex vertex) {
// List<Edge> toRemove = outgoing.remove(vertex);
// toRemove.addAll(incoming.remove(vertex));
// for (Edge e : toRemove)
// if (e instanceof Edge)
// removeEdge((Edge)e);
// }
public void removeVertex(Vertex vertex) {
outgoing.remove(vertex);
incoming.remove(vertex);
}
public int getDegreeIn(Vertex v) {
List<Edge> l = incoming.get(v);
if (l == null)
return 0;
else
return l.size();
}
public int getDegreeOut(Vertex v) {
List<Edge> l = outgoing.get(v);
if (l == null)
return 0;
else
return l.size();
}
public boolean containsVertex(Vertex vertex) {
return outgoing.containsKey(vertex) || incoming.containsKey(vertex);
}
private void writeObject(ObjectOutputStream out) throws IOException {
for (List<Edge> le : outgoing.values())
((ArrayList<Edge>)le).trimToSize();
for (List<Edge> le : incoming.values())
((ArrayList<Edge>)le).trimToSize();
out.defaultWriteObject();
}
}