de.tuilmenau.ics.graph.RoutableGraph.java Source code

Java tutorial

Introduction

Here is the source code for de.tuilmenau.ics.graph.RoutableGraph.java

Source

/*******************************************************************************
 * Graph
 * Copyright (C) 2012, Integrated Communication Systems Group, TU Ilmenau.
 * 
 * This program and the accompanying materials are dual-licensed under either
 * the terms of the Eclipse Public License v1.0 as published by the Eclipse
 * Foundation
 *  
 *   or (per the licensee's choosing)
 *  
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation.
 ******************************************************************************/
package de.tuilmenau.ics.graph;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import org.apache.commons.collections15.Transformer;

import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.SparseMultigraph;
import edu.uci.ics.jung.graph.util.EdgeType;

/**
 * Stores graph and allows routing operations.
 */
public class RoutableGraph<NodeObject, LinkObject> extends Observable implements Observer {
    public enum EventType {
        ADDED, REMOVED, UPDATED
    };

    /**
     * Enables the caching of routing decisions. The caching leads to
     * high memory consumption in large networks. Therefore, it should
     * only be activated if the scenario is reasonable small.
     */
    private static final boolean ENABLE_CACHING = false;

    public class Event {
        public Event(EventType type, Object obj) {
            this.type = type;
            this.obj = obj;
        }

        @Override
        public String toString() {
            return obj.toString() + " " + type;
        }

        public EventType type;
        public Object obj;
    }

    /**
     * Constructor for map with constant link weights equal to 1
     */
    public RoutableGraph() {
        this(null);
    }

    /**
     * Constructor for dynamic link weights derived from a transformer
     * 
     * @param pLinkToValueTransformer Transformer from links to link weight values
     */
    public RoutableGraph(LinkTransformer<LinkObject> pLinkToValueTransformer) {
        super();

        mNodes = new SparseMultigraph<NodeObject, LinkObject>();

        // is transformer valid?
        if (pLinkToValueTransformer != null) {
            mLinkToValueTransformer = new LinkTransformerAdapter(pLinkToValueTransformer);
        }
        // else: mLinkToValueTransformer remains null
    }

    private class LinkTransformerAdapter implements Transformer<LinkObject, Number> {
        public LinkTransformerAdapter(LinkTransformer<LinkObject> transformer) {
            this.transformer = transformer;
        }

        @Override
        public Number transform(LinkObject input) {
            return transformer.transform(input);
        }

        private LinkTransformer<LinkObject> transformer;
    }

    public Collection<NodeObject> getVertices() {
        LinkedList<NodeObject> tNodes = new LinkedList<NodeObject>();
        synchronized (mNodes) {
            for (NodeObject tObj : mNodes.getVertices()) {
                tNodes.add(tObj);
            }
        }
        return tNodes;
    }

    public int getNumberVertices() {
        return mNodes.getVertexCount();
    }

    public Collection<LinkObject> getEdges() {
        LinkedList<LinkObject> tCollection = new LinkedList<LinkObject>();
        synchronized (mNodes) {
            for (LinkObject tLink : mNodes.getEdges()) {
                tCollection.add(tLink);
            }
        }
        return tCollection;
    }

    public int getNumberEdges() {
        return mNodes.getEdgeCount();
    }

    public NodeObject getDest(LinkObject pLink) {
        return mNodes.getDest(pLink);
    }

    public NodeObject getSource(LinkObject pLink) {
        return mNodes.getSource(pLink);
    }

    public synchronized Collection<LinkObject> getOutEdges(NodeObject pNode) {
        Collection<LinkObject> tEdges = null;
        for (NodeObject tNode : mNodes.getVertices()) {
            if (pNode.equals(tNode)) {
                tEdges = mNodes.getOutEdges(tNode);
                break;
            }
        }

        return tEdges;
    }

    /**
     * Method for GUI purposes ONLY!
     * 
     * @return Reference to plain graph object (for read access only)
     */
    public Graph<NodeObject, LinkObject> getGraphForGUI() {
        return mNodes;
    }

    /**
     * Used to remove all elements without notifying anybody about that.
     * Needed for exiting a simulation at any time.
     */
    public void cleanup() {
        Collection<NodeObject> tNodes = getVertices();
        while (!tNodes.isEmpty()) {
            NodeObject tNode = tNodes.iterator().next();
            mNodes.removeVertex(tNode);
        }
    }

    /**
     * Adds node to topology, if it is not already included.
     * 
     * @param pNode node to include
     * @return node object used for structure (object added previously but equal to pNode OR pNode if it is new)
     */
    public synchronized NodeObject add(NodeObject pNode) {
        NodeObject tRes = null;

        if (pNode != null) {
            tRes = containsVertex(pNode);

            if (tRes == null) {
                mNodes.addVertex(pNode);
                mResetRouting = true;
                tRes = pNode;

                // register map as an observer of object
                if (pNode instanceof Observable) {
                    ((Observable) pNode).addObserver(this);
                }

                // inform observer of map about new object
                notifyObservers(new Event(EventType.ADDED, pNode));
            }
        }

        return tRes;
    }

    public synchronized boolean contains(NodeObject pNode) {
        return (containsVertex(pNode) != null);
    }

    public synchronized boolean remove(NodeObject pNode) {
        NodeObject tUsedObj = containsVertex(pNode);

        if (tUsedObj != null) {
            boolean tRes = mNodes.removeVertex(tUsedObj);

            mResetRouting = true;

            // remove map as an observer from object
            if (pNode instanceof Observable) {
                ((Observable) pNode).deleteObserver(this);
            }

            // inform observer of map about deletion
            if (tRes)
                notifyObservers(new Event(EventType.REMOVED, pNode));

            return tRes;
        } else
            return false;
    }

    /**
     * Links two nodes. If nodes not exists, they will be created.
     * Method do NOT check, if link already exists.
     * 
     * @param pFrom link starts at
     * @param pTo links end at
     * @param pLinkValue link object
     */
    public synchronized void link(NodeObject pFrom, NodeObject pTo, LinkObject pLinkValue) {
        if ((pFrom != null) && (pTo != null) && (pLinkValue != null)) {
            // get equivalent object used for map for pFrom and pTo:

            pFrom = add(pFrom);
            pTo = add(pTo);

            if (mNodes.addEdge(pLinkValue, pFrom, pTo, EdgeType.DIRECTED)) {
                mResetRouting = true;
                notifyObservers(new Event(EventType.ADDED, pLinkValue));
            }
        }
    }

    /**
     * Checks whether there is a direct edge between two nodes. Optionally, the
     * link between both can be checked, too.
     * 
     * @param pFrom From node (!= null)
     * @param pTo To node (!= null)
     * @param pLinkValueTemplate null=any link between the nodes is sufficient; otherwise, it has to equal the parameter 
     * @return If the two nodes are directly connected
     */
    public boolean isDirectEdge(NodeObject pFrom, NodeObject pTo, LinkObject pLinkValueTemplate) {
        return (getEdge(pFrom, pTo, pLinkValueTemplate) != null);
    }

    /**
     * Searches for a link object between two nodes. Optionally, the
     * link between both can be checked, if it equals an search template.
     * 
     * @param pFrom From node (!= null)
     * @param pTo To node (!= null)
     * @param pLinkValueTemplate null=any link between the nodes is sufficient; otherwise, it has to equal the parameter 
     * @return First object matches the search parameters; null if no edge available
     */
    public synchronized LinkObject getEdge(NodeObject pFrom, NodeObject pTo, LinkObject pLinkValueTemplate) {
        if ((pFrom != null) && (pTo != null)) {
            // get equivalent object used for map for pFrom and pTo:
            Collection<LinkObject> tOutEdges = getOutEdges(pFrom);

            for (LinkObject tLink : tOutEdges) {
                NodeObject tTo = getDest(tLink);

                if (tTo != null) {
                    if (tTo.equals(pTo)) {
                        // check optional link object; if not available
                        // we just look for the nodes
                        if (pLinkValueTemplate != null) {
                            if (pLinkValueTemplate.equals(tLink)) {
                                return tLink;
                            }
                        } else {
                            return tLink;
                        }
                    }
                    // else: strange; ignore it
                }
            }
        }

        return null;
    }

    public synchronized boolean unlink(LinkObject pLinkValue) {
        boolean tRes = mNodes.removeEdge(pLinkValue);

        if (tRes) {
            mResetRouting = true;
            notifyObservers(new Event(EventType.REMOVED, pLinkValue));
        }

        return tRes;
    }

    public synchronized void edgeWeightChanged(LinkObject pLink) {
        mResetRouting = true;
    }

    public synchronized List<LinkObject> getRoute(NodeObject pFrom, NodeObject pTo) {
        List<LinkObject> tPath = null;

        pFrom = containsVertex(pFrom);
        pTo = containsVertex(pTo);

        if ((pFrom != null) && (pTo != null)) {
            // is there an old algorithm object, which can be reused?
            if (mRoutingAlg == null) {
                mResetRouting = false;

                // either Dijkstra with variable link weights or constant link weights equal to 1
                if (mLinkToValueTransformer != null)
                    mRoutingAlg = new DijkstraShortestPath<NodeObject, LinkObject>(mNodes, mLinkToValueTransformer,
                            ENABLE_CACHING);
                else
                    mRoutingAlg = new DijkstraShortestPath<NodeObject, LinkObject>(mNodes, ENABLE_CACHING);
            } else {
                // are there any changes and we have to remove the cached values?
                if (ENABLE_CACHING) {
                    if (mResetRouting) {
                        mRoutingAlg.reset();
                        mResetRouting = false;
                    }
                }
            }

            tPath = mRoutingAlg.getPath(pFrom, pTo);

            if (!ENABLE_CACHING) {
                // cleanup data structure; somehow that does not work, if
                // "chached" is turned of in constructor only.
                mRoutingAlg.reset();
            }
        }

        return tPath;
    }

    /**
     * Method is called by objects observed by the map. It is triggering an
     * update event for the observers of the map. In special, this mechanism
     * enables the GUI to redraw after the map or an object of the map has
     * changed.
     */
    @Override
    public void update(Observable observable, Object parameter) {
        notifyObservers(new Event(EventType.UPDATED, observable));
    }

    /**
     * Replaces mNodes.containsVertex because we have to use equals to compare the
     * objects. This is needed due to the usage with RMI.
     * 
     * Note: In order to avoid parallel changes in the vertex list, the access
     *       to that list (add, contains, remove) must by synchronized.
     * 
     * @param pNewNode node to search for
     * @return found node in structure equal to pNewNode OR null
     */
    protected NodeObject containsVertex(NodeObject pNewNode) {
        if (pNewNode == null) {
            throw new NullPointerException("TopologyMap.containsVertex with null argument (" + this + ")");
        }

        // replaces mNode.containsVertex, which is doing only a reference comparison
        for (NodeObject tNode : mNodes.getVertices()) {
            if (pNewNode.equals(tNode)) {
                return tNode;
            }
        }

        return null;
    }

    /**
    * Checks whether two nodes are directly linked with each other. Optionally, the
    * link between both can be checked, too.
    * 
    * @param pFrom From node (!= null)
    * @param pTo To node (!= null)
    * @param pLinkValue Optional; null=any link between the nodes is sufficient
    * @return If the two nodes are directly connected
    */
    public synchronized boolean isLinked(NodeObject pFrom, NodeObject pTo, LinkObject pLinkValue) {
        boolean tRes = false;

        if ((pFrom != null) && (pTo != null)) {
            // get equivalent object used for map for pFrom and pTo:
            Collection<LinkObject> tOutEdges = getOutEdges(pFrom);
            if (tOutEdges != null) {
                for (LinkObject tLink : tOutEdges) {
                    NodeObject tTo = getDest(tLink);

                    if (tTo != null) {
                        if (tTo.equals(pTo)) {
                            // check optional link object; if not available
                            // we just look for the nodes
                            if (pLinkValue != null) {
                                if (pLinkValue.equals(tLink)) {
                                    return true;
                                }
                            } else {
                                return true;
                            }
                        }
                    }
                    // else: strange; ignore it
                }
            }
        }
        return tRes;
    }

    public synchronized boolean isLinked(NodeObject pFrom, NodeObject pTo) {
        return mNodes.isNeighbor(pFrom, pTo);
    }

    public synchronized void notifyObservers(Object pEvent) {
        setChanged();
        super.notifyObservers(pEvent);
    }

    protected Graph<NodeObject, LinkObject> mNodes = null;
    private Transformer<LinkObject, Number> mLinkToValueTransformer = null;

    private DijkstraShortestPath<NodeObject, LinkObject> mRoutingAlg = null;
    private boolean mResetRouting = false;
}