Java tutorial
/* * This file is part of qSim. * * qSim is 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. * * qSim 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 qSim. If not, see <http://www.gnu.org/licenses/>. */ package sk.stuba.fiit.kvasnicka.topologyvisual.topology; import edu.uci.ics.jung.algorithms.layout.AbstractLayout; import edu.uci.ics.jung.algorithms.layout.StaticLayout; import edu.uci.ics.jung.graph.AbstractGraph; import edu.uci.ics.jung.graph.Graph; import edu.uci.ics.jung.graph.UndirectedSparseGraph; import edu.uci.ics.jung.visualization.BasicVisualizationServer; import edu.uci.ics.jung.visualization.control.*; import edu.uci.ics.jung.visualization.decorators.DefaultVertexIconTransformer; import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer; import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer; import edu.uci.ics.jung.visualization.picking.PickedState; import edu.uci.ics.jung.visualization.picking.ShapePickSupport; import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer; import edu.uci.ics.jung.visualization.renderers.Renderer; import java.awt.*; import java.util.List; import java.awt.event.InputEvent; import java.util.Collection; import java.util.Map; import java.util.Set; import javax.swing.JComponent; import javax.swing.event.EventListenerList; import lombok.Getter; import org.apache.commons.collections15.Transformer; import org.apache.log4j.Logger; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import sk.stuba.fiit.kvasnicka.topologyvisual.PreferenciesHelper; import sk.stuba.fiit.kvasnicka.topologyvisual.exceptions.RoutingException; import sk.stuba.fiit.kvasnicka.topologyvisual.filetype.gui.TopologyVisualisation; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.MyVisualizationViewer; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.edges.TopologyEdge; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.events.vertexcreated.VertexCreatedEvent; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.events.vertexcreated.VertexCreatedListener; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.events.vertexdeleted.VertexDeletedEvent; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.events.vertexdeleted.VertexDeletedListener; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.utils.*; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.vertices.TopologyVertex; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.vertices.utils.MyVertexIconShapeTransformer; import sk.stuba.fiit.kvasnicka.topologyvisual.graph.vertices.utils.VertexToIconTransformer; import sk.stuba.fiit.kvasnicka.topologyvisual.gui.NetbeansWindowHelper; import sk.stuba.fiit.kvasnicka.topologyvisual.gui.palette.TopologyPaletteTopComponent; import sk.stuba.fiit.kvasnicka.topologyvisual.route.RoutingHelper; import sk.stuba.fiit.kvasnicka.topologyvisual.serialisation.DeserialisationResult; /** * This class is graphical representation of network topology it uses JUNG * library * * @author Igor Kvasnicka */ @NbBundle.Messages({ "cycle_exception=No cycles are allowed in the route" }) public class Topology implements VertexCreatedListener { private static Logger logg = Logger.getLogger(Topology.class); private final TopologyModeEnum DEFAULT_MODE = TopologyModeEnum.CREATION; @Getter private AbstractGraph<TopologyVertex, TopologyEdge> g; @Getter private AbstractLayout<TopologyVertex, TopologyEdge> layout; private PopupVertexEdgeMenuMousePlugin popupVertexMenuMousePlugin; private MyVisualizationViewer vv; private MyGraphMouse graphMouse; @Getter private TopologyVertexFactory vertexFactory; private transient RoutingHelper routingHelper = new RoutingHelper(); @Getter private final TopologyVisualisation topolElementTopComponent; @Getter private TopologyModeEnum topologyMode = null; private GraphMouseListener<TopologyVertex> vertexPickedListener; private DefaultModalGraphMouse defaultGm; private EventListenerList listenerList = new EventListenerList(); @Getter /** * sometimes I need to know single selected vertex (so i do not care about * multiselect) */ private TopologyVertex selectedSingleVertex = null; /** * creates new instance * * @param mainFrame instance of MainFrame */ public Topology(TopologyVisualisation topolElementTopComponent) { this.topolElementTopComponent = topolElementTopComponent; } public void setSelectedSingleVertex(TopologyVertex selectedSingleVertex) { this.selectedSingleVertex = selectedSingleVertex; //update copy action button in toolbar if (selectedSingleVertex == null) { topolElementTopComponent.updateCopyButton(false); } else { topolElementTopComponent.updateCopyButton(true); } } /** * sets topology to its default mode */ public void setDefaultMode() { setMode(DEFAULT_MODE); } /** * retrieves all selected vertices * * @return */ public Set<TopologyVertex> getSelectedVertices() { return vv.getPickedVertexState().getPicked(); } /** * retrieves all selected edges * * @return */ public Set<TopologyEdge> getSelectedEdges() { return vv.getPickedEdgeState().getPicked(); } /** * sets topology into specified mode. each mode is used for different * purpose. in fact mode means set of JUNG plugins * * @param mode */ public void setMode(TopologyModeEnum mode) { if (mode == topologyMode) {//mode did not change return; } clearRoutingMode(); clearTopologyCreateMode(); switch (mode) { case CREATION: initTopologyCreateMode(); break; case SIMULATION: break; case SIMULATION_RULES: initTopologySimulationRulesCreationMode(); break; case ROUTING: initTopologyRoutingMode(); break; default: throw new IllegalStateException("unkown TopologyModeEnum"); } topologyMode = mode; } /** * reverts all changes made by Routing mode */ private void clearRoutingMode() { vv.getRenderContext().setEdgeDrawPaintTransformer( new PickableEdgePaintTransformer<TopologyEdge>(vv.getPickedEdgeState(), Color.black, Color.cyan)); } private void clearTopologyCreateMode() { if (defaultGm == null) { defaultGm = new DefaultModalGraphMouse(); defaultGm.setMode(ModalGraphMouse.Mode.TRANSFORMING); } vv.setGraphMouse(defaultGm); TopologyPaletteTopComponent component = (TopologyPaletteTopComponent) WindowManager.getDefault() .findTopComponent("TopologyPaletteTopComponent"); if (component == null) { logg.error("Could not find component TopologyPaletteTopComponent"); return; } component.setEnabledPalette(false); } /** * programatically (manually) select specified vertex * * @param v * @param selected */ public void manuallySelectVertex(TopologyVertex v, boolean selected) { vv.getPickedVertexState().pick(v, selected); } /** * programatically (manually) select specified edge * * @param e * @param selected */ public void manuallySelectEdge(TopologyEdge e, boolean selected) { vv.getPickedEdgeState().pick(e, selected); } @Override public void vertexCreatedOccurred(VertexCreatedEvent evt) { logg.debug("vertex created"); topolElementTopComponent.getTopologyElementCreator().cancelAction(); topolElementTopComponent.paletteClearSelection(); } /** * initialises JUNG stuff. here are all default plugins initialised * */ public void initTopology() { logg.debug("init jung - creation"); //vertex as icon Transformer<TopologyVertex, Paint> vpf = new PickableVertexPaintTransformer<TopologyVertex>( vv.getPickedVertexState(), Color.white, Color.yellow); vv.getRenderContext().setVertexFillPaintTransformer(vpf); vv.getRenderContext().setEdgeDrawPaintTransformer( new PickableEdgePaintTransformer<TopologyEdge>(vv.getPickedEdgeState(), Color.black, Color.cyan)); final MyVertexIconShapeTransformer<TopologyVertex> vertexImageShapeFunction = new MyVertexIconShapeTransformer<TopologyVertex>(); final DefaultVertexIconTransformer<TopologyVertex> vertexIconFunction = new VertexToIconTransformer<TopologyVertex>(); vv.getRenderContext().setVertexShapeTransformer(vertexImageShapeFunction); vv.getRenderContext().setVertexIconTransformer(vertexIconFunction); //tooltips over the vertex vv.setVertexToolTipTransformer(new Transformer<TopologyVertex, String>() { @Override public String transform(TopologyVertex topologyVertex) { if (!PreferenciesHelper.isNodeTooltipName() && !PreferenciesHelper.isNodeTooltipDescription()) { return null; } StringBuilder sb = new StringBuilder("<html>"); if (PreferenciesHelper.isNodeTooltipName()) { sb.append("<b>Name: </b>").append(topologyVertex.getName()); } if (PreferenciesHelper.isNodeTooltipDescription()) { if (PreferenciesHelper.isNodeTooltipName()) { sb.append("<br>"); } sb.append("<b>Description: </b>").append(topologyVertex.getDescription()); } sb.append("</html>"); return sb.toString(); } }); vv.getRenderContext().setVertexLabelRenderer(new MyVertexLabelRenderer(Color.BLACK)); vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.S); vv.getRenderContext().setVertexLabelTransformer(new Transformer<TopologyVertex, String>() { @Override public String transform(TopologyVertex v) { if (PreferenciesHelper.isShowNodeNamesInTopology()) { return v.getName(); } return null; } }); //picking support, so that vertices can be selected vv.setPickSupport(new ShapePickSupport<TopologyVertex, TopologyEdge>(vv)); PickedState<TopologyVertex> pickedState = vv.getPickedVertexState(); pickedState.addItemListener( new VertexPickedTopolCreationListener(vertexIconFunction, topolElementTopComponent, pickedState)); } /** * inits stuff related to Creation Mode */ private void initTopologyCreateMode() { //init mouse initMouseControlTopologyCreation(topolElementTopComponent); //palette TopologyPaletteTopComponent component = (TopologyPaletteTopComponent) WindowManager.getDefault() .findTopComponent("TopologyPaletteTopComponent"); if (component == null) { logg.error("Could not find component TopologyPaletteTopComponent"); return; } component.setEnabledPalette(true); } /** * initializes mouse controls for topology creation * * @param mainFrame reference to MainFrame object */ private void initMouseControlTopologyCreation(TopologyVisualisation topolElementTopComponent) { if (graphMouse == null) { graphMouse = new MyGraphMouse(vv.getRenderContext(), vertexFactory, this); graphMouse.addVertexCreatedListener(this); graphMouse.addVertexCreatedListener(topolElementTopComponent); graphMouse.add(new TranslatingGraphMousePlugin(InputEvent.BUTTON3_MASK)); popupVertexMenuMousePlugin = new PopupVertexEdgeMenuMousePlugin(this); graphMouse.add(popupVertexMenuMousePlugin); graphMouse.setZoomAtMouse(true); } graphMouse.setMode(ModalGraphMouse.Mode.PICKING); vv.setGraphMouse(graphMouse); } /** * initialises JUNG stuff when user is creating simulation rules * * @param mainFrame reference to MainFrame object */ private void initTopologySimulationRulesCreationMode() { PickedState<TopologyVertex> ps = vv.getPickedVertexState(); vv.removeGraphMouseListener(vertexPickedListener); DefaultVertexIconTransformer<TopologyVertex> vertexIconTransformer = (DefaultVertexIconTransformer<TopologyVertex>) vv .getRenderContext().getVertexIconTransformer(); vertexPickedListener = new VertexPickedSimulRulesListener(vertexIconTransformer, topolElementTopComponent, topolElementTopComponent.getAddSimulRuleTopComponent(), ps); vv.addGraphMouseListener(vertexPickedListener); } /** * inits plugins for routing mode */ private void initTopologyRoutingMode() { } /** * deletes vertex from JUNG topology * * @param vertex vertex to delete */ public void deleteVertex(TopologyVertex vertex) { vertexFactory.deleteVertex(vertex); g.removeVertex(vertex); getVv().repaint(); topolElementTopComponent.topologyModified(); fireVertexDeletedEvent(new VertexDeletedEvent(this, vertex)); } public void addVertex(TopologyVertex newVertex, Point location) { Graph<TopologyVertex, TopologyEdge> graph = vv.getModel().getGraphLayout().getGraph(); graph.addVertex(newVertex); layout.setLocation(newVertex, vv.getRenderContext().getMultiLayerTransformer().inverseTransform(location)); vv.repaint(); fireVertexCreatedEvent(new VertexCreatedEvent(this, newVertex)); } /** * deletes multiple vertices from JUNG topology * * @param vertex vertex to delete */ public void deleteVertex(Collection<TopologyVertex> vertices) { for (TopologyVertex vertex : vertices) { vertexFactory.deleteVertex(vertex); g.removeVertex(vertex); } getVv().repaint(); topolElementTopComponent.topologyModified(); fireVertexDeletedEvent(new VertexDeletedEvent(this, vertices)); } /** * adds new edge into the topology all edges are not-oriented * * @param begin starting vertex * @param end ending vertex * @param edge instance of TopologyEdge that defines edge */ public void addEdge(TopologyVertex begin, TopologyVertex end, TopologyEdge edge) { g.addEdge(edge, begin, end); topolElementTopComponent.paletteClearSelection(); } /** * check that this edge is not forbidden * * @param v1 first end of the edge * @param v2 second end of the edge * @return null of edge is allowed; v1 or v2 depending on which of them * blocks the edge (if both, then v1 is returned) */ public TopologyVertex isEdgeAllowed(TopologyVertex v1, TopologyVertex v2) { TopologyEdge dummyEdge = new TopologyEdge(null, v1, v2);//create a dummy edge for JUNG, so I will be able to get neighbours g.addEdge(dummyEdge, v1, v2); //tests vertex v1 Map<Class, Integer> routingRule1 = v1.getDataModel().getRoutingRules(); for (Class c : routingRule1.keySet()) { int count = RoutingHelper.getNumberOfNeighboursByType(this, v1, c); if (count > routingRule1.get(c)) { //do not forget do delete the dummy edge g.removeEdge(dummyEdge); return v1; } } //tests vertex v2 routingRule1 = v2.getDataModel().getRoutingRules(); for (Class c : routingRule1.keySet()) { int count = RoutingHelper.getNumberOfNeighboursByType(this, v2, c); if (count > routingRule1.get(c)) { //do not forget do delete the dummy edge g.removeEdge(dummyEdge); return v2; } } //do not forget do delete the dummy edge g.removeEdge(dummyEdge); return null; } /** * deletes edge from JUNG topology * * @param edge edge to delete */ public void deleteEdge(TopologyEdge edge) { g.removeEdge(edge); getVv().repaint(); topolElementTopComponent.paletteClearSelection(); topolElementTopComponent.topologyModified(); } /** * deletes edge from JUNG topology * * @param edge edge to delete */ public void deleteEdge(Collection<TopologyEdge> edges) { for (TopologyEdge edge : edges) { g.removeEdge(edge); } getVv().repaint(); topolElementTopComponent.paletteClearSelection(); topolElementTopComponent.topologyModified(); } /** * getter for BasicVisualizationServer * * @return BasicVisualizationServer instance */ public BasicVisualizationServer<TopologyVertex, TopologyEdge> getVv() { return vv; } /** * sets JUNG's mode to PICIKNG, so that new vertices can be selected * * @see #setEditingMode() * @see #setTransformingMode() */ public void setPickingMode() { graphMouse.setMode(ModalGraphMouse.Mode.PICKING); } /** * sets JUNG's mode to PICIKNG, so that user can move around the topology * * @see #setEditingMode() * @see #setPickingMode() */ public void setTransformingMode() { graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); } public void repaintGraph() { vv.repaint(); } public boolean edgeExists(TopologyVertex v1, TopologyVertex v2) { if (g.findEdge(v1, v2) == null) { return false; } return true; } /** * if no settings were loaded from file */ public void createDefaultSettings() { vertexFactory = new TopologyVertexFactory(topolElementTopComponent); g = new UndirectedSparseGraph<TopologyVertex, TopologyEdge>(); layout = new StaticLayout<TopologyVertex, TopologyEdge>(g); vv = new MyVisualizationViewer(layout); vv.setBackground(Color.WHITE); topolElementTopComponent.addJungIntoFrame(vv); } /** * this topology was created from file * * @param loadSettings */ public void loadFromSettings(DeserialisationResult loadSettings) { if (loadSettings == null) { return; } vertexFactory = loadSettings.getVertexFactory(); g = loadSettings.getG(); layout = loadSettings.getLayout(); layout.setGraph(g); vv = new MyVisualizationViewer(layout); vv.setBackground(Color.WHITE); topolElementTopComponent.addJungIntoFrame(vv); } /** * higlights edges from one vertex to other. to computer edges between these * two vertices will be used algorithm specified in file settings. This * method may be used only when Topology is in ROUTING mode * * @param sourceVertex * @param destinationVertex * @param vertices between them * @param distanceVector true if distance vector routing protocol */ public void highlightEdgesFromTo(TopologyVertex source, TopologyVertex destination, List<TopologyVertex> fixedVertices, boolean distanceVector) throws RoutingException { if (TopologyModeEnum.ROUTING != topologyMode) { return; } //first retirieve edges between these two vertices final Collection<TopologyEdge> edges = RoutingHelper.retrieveEdges(getG(), source, destination, distanceVector, fixedVertices); if (!routingHelper.checkRouteForCycle(edges)) { throw new RoutingException(NbBundle.getMessage(Topology.class, "cycle_exception")); } //now highlight each edge vv.getRenderContext().setEdgeDrawPaintTransformer(new Transformer<TopologyEdge, Paint>() { @Override public Paint transform(TopologyEdge i) { for (TopologyEdge e : edges) { if (i == e) { //they are literally the same - the same object return Color.BLUE; } } return Color.BLACK; } }); vv.repaint(); //and do not forget to highlight source and destination vertices, too } /** * deselects all selected vertices */ public void deselectVertices() { if (NetbeansWindowHelper.getInstance().getActiveTopology() == null) { return; } vv.getPickedVertexState().clear(); for (TopologyVertex vertex : NetbeansWindowHelper.getInstance().getActiveTopology().getVertexFactory() .getAllVertices()) { vertex.deSelectVertex(); vertex.deCheckVertex(); } } /** * deselects all selected edges */ public void deselectEdges() { vv.getPickedEdgeState().clear(); } public void addVertexCreatedListener(VertexCreatedListener listener) { graphMouse.addVertexCreatedListener(listener); } public void removeVertexCreatedListener(VertexCreatedListener listener) { graphMouse.removeVertexCreatedListener(listener); } private void fireVertexCreatedEvent(VertexCreatedEvent event) { graphMouse.fireVertexCreatedEvent(event); } public void addVertexDeletedListener(VertexDeletedListener listener) { listenerList.add(VertexDeletedListener.class, listener); } public void removeVertexDeletedListener(VertexDeletedListener listener) { listenerList.remove(VertexDeletedListener.class, listener); } private void fireVertexDeletedEvent(VertexDeletedEvent evt) { Object[] listeners = listenerList.getListenerList(); // Each listener occupies two elements - the first is the listener class // and the second is the listener instance for (int i = 0; i < listeners.length; i += 2) { if (listeners[i] == VertexDeletedListener.class) { ((VertexDeletedListener) listeners[i + 1]).vertexDeletedOccurred(evt); } } } /** * this is renderer for vertex labels. the problem with a default one is * that when vertex is selected, label is blue - I do not want this * behaviour, because I do not use Pick mouse plugin */ public static class MyVertexLabelRenderer extends DefaultVertexLabelRenderer { private Color background_color; private Color foreground_color; public MyVertexLabelRenderer(Color pickedVertexLabelColor) { super(pickedVertexLabelColor); foreground_color = Color.BLACK; background_color = Color.WHITE; } @Override public <V> Component getVertexLabelRendererComponent(JComponent vv, Object value, Font font, boolean isSelected, V vertex) { Component res = super.getVertexLabelRendererComponent(vv, value, font, isSelected, vertex); res.setForeground(foreground_color); res.setBackground(background_color); return res; } } public enum TopologyModeEnum { CREATION, //creating new topology SIMULATION_RULES, //defining simulation rules ROUTING, //used when defining routing path SIMULATION//simulation running } }