/* * This file is part of the Trickl Open Source Libraries. * * Trickl Open Source Libraries - http://open.trickl.com/ * * Copyright (C) 2011 Tim Gee. * * Trickl Open Source Libraries are free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Trickl Open Source Libraries are 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 project. If not, see <http://www.gnu.org/licenses/>. */ package com.trickl.graph.planar; import com.trickl.graph.CopyEdgeFactory; import com.trickl.graph.CopyVertexFactory; import com.trickl.graph.edges.DirectedEdge; import java.util.HashMap; import java.util.Map; public class PlanarCopyGraphVisitor<V1, E1, V2, E2> extends AbstractPlanarFaceTraversalVisitor<V1, E1> { protected PlanarGraph<V1, E1> inputGraph; protected PlanarGraph<V2, E2> outputGraph; protected CopyVertexFactory<V2, V1> vertexFactory; protected CopyEdgeFactory<V2, E2, E1> edgeFactory; private Map<V1, V2> vertexMap; private Map<V1, Integer> aggregationGroups; private Map<Integer, V2> aggregationVertices; public PlanarCopyGraphVisitor(PlanarGraph<V1, E1> inputGraph, PlanarGraph<V2, E2> dualGraph) { this(inputGraph, dualGraph, null); } public PlanarCopyGraphVisitor(PlanarGraph<V1, E1> inputGraph, PlanarGraph<V2, E2> dualGraph, CopyVertexFactory<V2, V1> vertexFactory) { this(inputGraph, dualGraph, vertexFactory, null); } public PlanarCopyGraphVisitor(PlanarGraph<V1, E1> inputGraph, PlanarGraph<V2, E2> outputGraph, CopyVertexFactory<V2, V1> vertexFactory, CopyEdgeFactory<V2, E2, E1> edgeFactory) { this.inputGraph = inputGraph; this.outputGraph = outputGraph; this.vertexFactory = vertexFactory; this.edgeFactory = edgeFactory; this.vertexMap = new HashMap<V1, V2>(); this.aggregationVertices= new HashMap<Integer, V2>(); } @Override public void nextEdge(V1 inputSource, V1 inputTarget) { V2 outputSource = vertexMap.get(inputSource); V2 outputTarget = vertexMap.get(inputTarget); // Find the before and after vertices in the planar empedding (if this is not // a self-loop). V2 outputBefore = null; V2 outputAfter = null; if (!outputSource.equals(outputTarget)) { V1 currentSource = inputSource; V1 currentTarget = inputGraph.getPrevVertex(inputSource, inputTarget); while (!currentTarget.equals(inputTarget) || !currentSource.equals(inputSource)) { Integer aggregationGroup = aggregationGroups == null ? null : aggregationGroups.get(currentTarget); V2 outputVertex = aggregationGroup == null ? vertexMap.get(currentTarget) : aggregationVertices.get(aggregationGroup); if (outputVertex == outputSource) { currentSource = inputGraph.getPrevVertex(currentTarget, currentSource); // Swap source and target V1 temp = currentTarget; currentTarget = currentSource; currentSource = temp; } else if (outputVertex == outputTarget || !outputGraph.containsEdge(outputVertex, outputSource)) { currentTarget = inputGraph.getPrevVertex(currentSource, currentTarget); } else { outputBefore = outputVertex; break; } } currentTarget = inputTarget; currentSource = inputGraph.getNextVertex(inputSource, inputTarget); while (!currentTarget.equals(inputTarget) || !currentSource.equals(inputSource)) { Integer aggregationGroup = aggregationGroups == null ? null : aggregationGroups.get(currentSource); V2 outputVertex = aggregationGroup == null ? vertexMap.get(currentSource) : aggregationVertices.get(aggregationGroup); if (outputVertex == outputTarget) { currentTarget = inputGraph.getNextVertex(currentTarget, currentSource); // Swap source and target V1 temp = currentTarget; currentTarget = currentSource; currentSource = temp; } else if (outputVertex == outputSource || !outputGraph.containsEdge(outputTarget, outputVertex)) { currentSource = inputGraph.getNextVertex(currentSource, currentTarget); } else { outputAfter = outputVertex; break; } } } // Don't create output self-loops, unless the self-loop existed // in the input graph. This allows us to create aggregate graphs without // self-loops for aggregated nodes. if (!outputSource.equals(outputTarget) || inputSource.equals(inputTarget)) { E2 edge = null; E1 inputEdge = inputGraph.getEdge(inputSource, inputTarget); if (edgeFactory != null) { edge = edgeFactory.createEdge(outputSource, outputTarget, inputEdge); } else { edge = (E2) inputEdge; } outputGraph.addEdge(outputSource, outputTarget, outputBefore, outputAfter, edge); } } @Override public void nextVertex(V1 inputVertex) { if (!vertexMap.containsKey(inputVertex)) { V2 outputVertex = null; Integer group = aggregationGroups == null ? null : aggregationGroups.get(inputVertex); if (group != null) { outputVertex = aggregationVertices.get(group); } if (outputVertex == null) { // A new vertex in the output graph if (vertexFactory != null) { // Use the vertex factory to create new vertices outputVertex = vertexFactory.createVertex(inputVertex); } else { // Can copy vertex straight over, but required V1 == V2 outputVertex = (V2) inputVertex; } outputGraph.addVertex(outputVertex); if (group != null) { // This vertex will now represent the group aggregationVertices.put(group, outputVertex); } } vertexMap.put(inputVertex, outputVertex); } } @Override public void endTraversal() { // Depending on the face traversal method, it cannot be guaranteed // that the boundary face was maintained after the edge copying // so we set it here. DirectedEdge<V1> inputBoundary = inputGraph.getBoundary(); // Need to test for null as inputGraph could have zero vertices. if (inputBoundary != null) { V2 outputBoundarySource = vertexMap.get(inputBoundary.getSource()); V2 outputBoundaryTarget = vertexMap.get(inputBoundary.getTarget()); if (outputGraph.containsEdge(outputBoundarySource, outputBoundaryTarget)) { outputGraph.setBoundary(outputBoundarySource, outputBoundaryTarget); } } } public Map<V1, V2> getVertexMap() { return vertexMap; } /** * Optional aggregation of input graph vertices. Putting multiple input * graph vertices in the same group will mean they are represented by * a single vertex in the output graph * @return */ public Map<V1, Integer> getAggregationGroups() { return aggregationGroups; } public void setAggregationGroups(Map<V1, Integer> aggregationGroups) { this.aggregationGroups = aggregationGroups; } }