/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.communication.model.internal; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.commons.logging.LogFactory; import de.rcenvironment.core.communication.common.NetworkGraphLink; import de.rcenvironment.core.communication.common.NetworkGraphWithProperties; import de.rcenvironment.core.communication.common.InstanceNodeSessionId; import de.rcenvironment.core.communication.model.NetworkRoutingInformation; import edu.uci.ics.jung.algorithms.filters.FilterUtils; import edu.uci.ics.jung.graph.DirectedSparseMultigraph; /** * A model representing a condensed, disconnected snapshot of the current network graph. Changes to the actual live network state will not * automatically affect this model. * * @author Robert Mischke */ public class NetworkGraphImpl implements NetworkGraphWithProperties { private final InstanceNodeSessionId localNodeId; private final Map<InstanceNodeSessionId, NetworkGraphNodeImpl> nodeMap = new HashMap<InstanceNodeSessionId, NetworkGraphNodeImpl>(); // TODO review: possible to reduce synchronization? - misc_ro private final DirectedSparseMultigraph<InstanceNodeSessionId, NetworkGraphLinkImpl> jungGraph; private NetworkRoutingInformation routingInformation; private String compactRepresentation; public NetworkGraphImpl(InstanceNodeSessionId localNodeId) { if (localNodeId == null) { throw new NullPointerException(); } this.localNodeId = localNodeId; this.jungGraph = new DirectedSparseMultigraph<InstanceNodeSessionId, NetworkGraphLinkImpl>(); addNode(localNodeId); } /** * Creates an induced subgraph from the given source graph, and a subset of vertices from that graph. * * Note that attached node properties are not transfered at this time, as this is usually performed before properties are attached. * * @param sourceGraph the source graph * @param subgraphVertices */ private NetworkGraphImpl(NetworkGraphImpl sourceGraph, Set<InstanceNodeSessionId> subgraphVertices) { this.localNodeId = sourceGraph.localNodeId; this.jungGraph = FilterUtils.createInducedSubgraph(subgraphVertices, sourceGraph.jungGraph); if (!jungGraph.containsVertex(localNodeId)) { throw new IllegalStateException(); } } /** * Creates a {@link NetworkGraphWithProperties} from the given source graph by attaching the given node properties. * * @param sourceGraph the source graph * @param nodeProperties the map of node properties to attach */ private NetworkGraphImpl(NetworkGraphImpl sourceGraph, Map<InstanceNodeSessionId, Map<String, String>> nodeProperties) { this.localNodeId = sourceGraph.localNodeId; // note: the graph object is shared! // TODO add "prevent modification" flag to graphs? this.jungGraph = sourceGraph.jungGraph; for (InstanceNodeSessionId nodeId : getNodeIds()) { NetworkGraphNodeImpl node = new NetworkGraphNodeImpl(nodeId, nodeProperties.get(nodeId)); if (nodeId.equals(localNodeId)) { node.setIsLocalNode(true); } nodeMap.put(nodeId, node); } } @Override public int getNodeCount() { synchronized (jungGraph) { return jungGraph.getVertexCount(); } } @Override public Set<InstanceNodeSessionId> getNodeIds() { // TODO cache if used frequently synchronized (jungGraph) { return new HashSet<InstanceNodeSessionId>(jungGraph.getVertices()); } } @Override public Collection<NetworkGraphNodeImpl> getNodes() { return Collections.unmodifiableCollection(nodeMap.values()); } @Override public NetworkGraphNodeImpl getNodeById(InstanceNodeSessionId nodeId) { return nodeMap.get(nodeId); } @Override public int getLinkCount() { synchronized (jungGraph) { return jungGraph.getEdgeCount(); } } @Override public Collection<? extends NetworkGraphLink> getLinks() { synchronized (jungGraph) { return new HashSet<NetworkGraphLink>(jungGraph.getEdges()); } } @Override public boolean containsLinkBetween(InstanceNodeSessionId sourceNodeId, InstanceNodeSessionId targetNodeId) { Collection<NetworkGraphLinkImpl> outEdges; synchronized (jungGraph) { outEdges = jungGraph.getOutEdges(sourceNodeId); } for (NetworkGraphLinkImpl edge : outEdges) { if (edge.getTargetNodeId().equals(targetNodeId)) { return true; } } return false; } /** * Returns an induced subgraph that consists only of nodes that are reachable from the local node. * * @return the reachable subgraph */ public NetworkGraphImpl reduceToReachableGraph() { generateRoutingInformation(); NetworkGraphImpl reachableGraph = new NetworkGraphImpl(this, routingInformation.getReachableNodes()); // valid for raw graph as well, so attach it there, too reachableGraph.setRoutingInformation(routingInformation); return reachableGraph; } @Override public NetworkGraphWithProperties attachNodeProperties(Map<InstanceNodeSessionId, Map<String, String>> nodeProperties) { return new NetworkGraphImpl(this, nodeProperties); } /** * Computes and returns the {@link NetworkRoutingInformation} for this graph. * * @return the {@link NetworkRoutingInformation} */ public NetworkRoutingInformation generateRoutingInformation() { if (routingInformation != null) { throw new IllegalStateException("Not expected to be called twice"); } routingInformation = new NetworkRoutingInformationImpl(this); return routingInformation; } @Override public NetworkRoutingInformation getRoutingInformation() { return routingInformation; } @Override public synchronized String getCompactRepresentation() { if (compactRepresentation == null) { compactRepresentation = generateCompactRepresentation(); } return compactRepresentation; } private String generateCompactRepresentation() { StringBuilder buffer = new StringBuilder(); // sort nodes by node id Set<InstanceNodeSessionId> sortedNodes = new TreeSet<InstanceNodeSessionId>(new Comparator<InstanceNodeSessionId>() { @Override public int compare(InstanceNodeSessionId o1, InstanceNodeSessionId o2) { return o1.getInstanceNodeSessionIdString().compareTo(o2.getInstanceNodeSessionIdString()); } }); Collection<InstanceNodeSessionId> unsortedNodes = getNodeIds(); sortedNodes.addAll(unsortedNodes); // detect collisions if (unsortedNodes.size() != sortedNodes.size()) { throw new IllegalStateException(); } buffer.append("Nodes: "); for (InstanceNodeSessionId node : sortedNodes) { buffer.append(node.getInstanceNodeSessionIdString()); buffer.append(" "); } // sort links by node id Set<NetworkGraphLink> sortedLinks = new TreeSet<NetworkGraphLink>(new Comparator<NetworkGraphLink>() { @Override public int compare(NetworkGraphLink o1, NetworkGraphLink o2) { return o1.getLinkId().compareTo(o2.getLinkId()); } }); Collection<? extends NetworkGraphLink> unsortedLinks = getLinks(); sortedLinks.addAll(unsortedLinks); // detect collisions if (unsortedLinks.size() != sortedLinks.size()) { throw new IllegalStateException(); } buffer.append("Links: "); for (NetworkGraphLink link : sortedLinks) { buffer.append(link.getLinkId()); buffer.append(":"); buffer.append(link.getSourceNodeId().getInstanceNodeSessionIdString()); buffer.append(">"); buffer.append(link.getTargetNodeId().getInstanceNodeSessionIdString()); buffer.append(" "); } buffer.append("\n"); String c = buffer.toString(); return c; } @Override public InstanceNodeSessionId getLocalNodeId() { return localNodeId; } /** * @param nodeId the graph node/vertex to add */ public void addNode(InstanceNodeSessionId nodeId) { // attach node properties if already present? synchronized (jungGraph) { if (jungGraph.containsVertex(nodeId)) { if (!nodeId.equals(localNodeId)) { LogFactory.getLog(getClass()).warn("Existing node added again: " + nodeId); } return; } jungGraph.addVertex(nodeId); } } /** * @param linkId the id of the new link * @param source the source node's id * @param target the target node's id */ public void addLink(String linkId, InstanceNodeSessionId source, InstanceNodeSessionId target) { addLink(new NetworkGraphLinkImpl(linkId, source, target)); } /** * @param link the graph link/edge to add */ public void addLink(NetworkGraphLinkImpl link) { synchronized (jungGraph) { jungGraph.addEdge(link, link.getSourceNodeId(), link.getTargetNodeId()); } } protected DirectedSparseMultigraph<InstanceNodeSessionId, NetworkGraphLinkImpl> getJungGraph() { synchronized (jungGraph) { return jungGraph; } } private void setRoutingInformation(NetworkRoutingInformation routingInformation) { this.routingInformation = routingInformation; } }