import cytoscape.CyNetwork; import cytoscape.Cytoscape; import cytoscape.data.FlagEvent; import cytoscape.data.FlagEventListener; import cytoscape.data.FlagFilter; import cytoscape.plugin.CytoscapePlugin; import cytoscape.util.CytoscapeAction; import giny.model.Edge; import giny.model.GraphPerspectiveChangeEvent; import giny.model.GraphPerspectiveChangeListener; import giny.model.Node; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * This plugin creates two menu options allowing the user to activate or * inactivate this plugin from the plugins menu of Cytoscape. The plugin * is activated on construction.
* * When active, this plugin links all of the networks in a Cytoscape instance. * Whenever a graph object is flagged or unflagged in one network, the object * is set to the same state in all the other networks that contain that object.
* * If an object is added to a network, it will be flagged iff the same object * exists and is flagged in another network. When a new network is created, * each graph object will be flagged in that network iff the same object is * flagged in an existing network. * * When deactivated, this plugin does not change the current flagged state of * any networks and no longer responds to any changes. * * When activated, this plugin synchronizes all of the existing networks by * flagging each graph object in every network that contains it if it is flagged * in any of them. */ public class MultiNetworkNodeSelection extends CytoscapePlugin implements PropertyChangeListener, GraphPerspectiveChangeListener, FlagEventListener { CytoscapeAction activateAction; //turns on this plugin CytoscapeAction deactivateAction; //turns off this plugin boolean working = false; //prevents responding to our own flag requests public MultiNetworkNodeSelection() { //creates two menu options for turning this plugin on and off activateAction = new ActivateMultiNetworkNodeSelectionAction(); activateAction.setPreferredMenu("Plugins"); deactivateAction = new DeactivateMultiNetworkNodeSelectionAction(); deactivateAction.setPreferredMenu("Plugins"); Cytoscape.getDesktop().getCyMenus().addAction(activateAction); Cytoscape.getDesktop().getCyMenus().addAction(deactivateAction); activateNetworkLinker(); //start with plugin activated } /** * Activates this plugin so that all Cytoscape networks are synchronized * in the flagged state of nodes and edges. This method first synchronizes * all the networks by flagging in all networks each object that is flagged * in any of them. It then attaches listeners to handle future changes and * switches which menu item is enabled. */ public void activateNetworkLinker() { Set flaggedNodes = getAllFlaggedNodes(); //all nodes flagged in any network Set flaggedEdges = getAllFlaggedEdges(); //all edges flagged in any network //iterate over all networks, flagging the sets built above that hold //all graph objects flagged in any network (objects that aren't in a //particular network just get skipped) //also add our listeners to these objects to catch future changes for (Iterator iter = Cytoscape.getNetworkSet().iterator(); iter.hasNext(); ) { CyNetwork network = (CyNetwork) iter.next(); network.setFlaggedNodes(flaggedNodes, true); network.setFlaggedEdges(flaggedEdges, true); //attach listeners network.addGraphPerspectiveChangeListener(this); network.addFlagEventListener(this); } //this catches network creation and destruction events Cytoscape.getSwingPropertyChangeSupport().addPropertyChangeListener(this); //swap which UI menu is enabled activateAction.setEnabled(false); deactivateAction.setEnabled(true); } /** * Deactivates this plugin so that future changes to one network will not be * propagated to all other networks. The current flagged state of graph objects * is not changed, and all listeners are detached. Also switches which menu item * is enabled. */ public void deactivateNetworkLinker() { //detach all the listeners from the networks for (Iterator iter = Cytoscape.getNetworkSet().iterator(); iter.hasNext(); ) { CyNetwork network = (CyNetwork) iter.next(); network.removeGraphPerspectiveChangeListener(this); network.removeFlagEventListener(this); } //don't pay attention to network creation/destruction events anymore Cytoscape.getSwingPropertyChangeSupport().removePropertyChangeListener(this); //swap which UI menu is enabled activateAction.setEnabled(true); deactivateAction.setEnabled(false); } /** * Returns a Set containing all the nodes that are flagged in any network. * Works properly even if this plugin is currently disabled. */ public Set getAllFlaggedNodes() { Set flaggedNodes = new HashSet(); //all nodes flagged in any network //iterate over all networks, saving the flagged objects in each network for (Iterator iter = Cytoscape.getNetworkSet().iterator(); iter.hasNext(); ) { CyNetwork network = (CyNetwork) iter.next(); Set nodes = network.getFlaggedNodes(); flaggedNodes.addAll(nodes); } return flaggedNodes; } /** * Returns a Set containing all the edges that are flagged in any network. * Works properly even if this plugin is currently disabled. */ public Set getAllFlaggedEdges() { Set flaggedEdges = new HashSet(); //all nodes flagged in any network //iterate over all networks, saving the flagged objects in each network for (Iterator iter = Cytoscape.getNetworkSet().iterator(); iter.hasNext(); ) { CyNetwork network = (CyNetwork) iter.next(); Set edges = network.getFlaggedEdges(); flaggedEdges.addAll(edges); } return flaggedEdges; } /** * When a CyNetwork is created, this method catches the event and flags * any graph objects that are flagged in another network. Does nothing * when a CyNetwork is destroyed, since it'll automatically discard its * set of listeners.
* * This method will not be called if this plugin is disabled, since the * listeners get detached. */ public void propertyChange(PropertyChangeEvent event) { if (event.getPropertyName() == Cytoscape.NETWORK_CREATED) { //get a reference to the network that was created String net_id = (String)event.getNewValue(); CyNetwork network = Cytoscape.getNetwork(net_id); if (network != null) { //flag objects in this network if they are flagged elsewhere Set flaggedNodes = getAllFlaggedNodes(); //flagged in any network network.setFlaggedNodes(flaggedNodes, true); Set flaggedEdges = getAllFlaggedEdges(); //flagged in any network network.setFlaggedEdges(flaggedEdges, true); //add listeners to catch future changes network.addGraphPerspectiveChangeListener(this); network.addFlagEventListener(this); } else { String lineSep = System.getProperty("line.separator"); String errString = "In MultiNetworkNodeSelection.propertyChange: " + lineSep + " unexpected null network processing NETWORK_CREATED event"; System.err.println(errString); } } } /** * Responds to flag events from a network's flagger by setting the same * state for the graph objects in all other networks. Does nothing if * the event was triggered by another method in this object.
* * This method will not be called if this plugin is disabled, since the * listeners get detached. */ public void onFlagEvent(FlagEvent event) { if (working) {return;} //don't respond to our own flag requests working = true; //set this variable to prevent responding to our flag requests FlagFilter source = event.getSource(); //iterate over all linked networks to set the same flagged state for (Iterator iter = Cytoscape.getNetworkSet().iterator(); iter.hasNext(); ) { CyNetwork network = (CyNetwork) iter.next(); FlagFilter filter = network.getFlagger(); //if this filter is the source of the event, skip it if (source == filter) {continue;} handleFlagEvent(event, network); } working = false; //listen to flag events again } /** * Given a FlagEvent representing flagging in one network, sets the * same flagged state in the supplied filter from another network. * Note that the FlagFilter makes sure the graph object actually exists * in the attached graph before flagging it. */ private void handleFlagEvent(FlagEvent event, CyNetwork network) { boolean flagOn = event.getEventType(); //true=flag on, false = flag off if (event.getTargetType() == FlagEvent.SINGLE_NODE) { network.setFlagged( (Node)event.getTarget(), flagOn ); } else if (event.getTargetType() == FlagEvent.SINGLE_EDGE) { network.setFlagged( (Edge)event.getTarget(), flagOn ); } else if (event.getTargetType() == FlagEvent.NODE_SET) { network.setFlaggedNodes( (Set)event.getTarget(), flagOn ); } else if (event.getTargetType() == FlagEvent.EDGE_SET) { network.setFlaggedEdges( (Set)event.getTarget(), flagOn ); } else {//huh? unknown target type //ignore for now } } /** * When nodes or edges are added to a linked CyNetwork, this method flags * those objects in the network that was changed if any linked network * currently flags those objects. */ public void graphPerspectiveChanged(GraphPerspectiveChangeEvent event) { working = true; //don't respond to our own flag requests CyNetwork source = (CyNetwork)event.getSource(); if (event.isNodesRestoredType()) {//nodes added to a graph Node[] newNodes = event.getRestoredNodes();//all restored nodes for (int n = 0; n < newNodes.length; n++) { Node nodeToCheck = newNodes[n]; //see if any linked network currently flags this node for (Iterator iter = Cytoscape.getNetworkSet().iterator(); iter.hasNext(); ) { CyNetwork network = (CyNetwork) iter.next(); if (network == source) {continue;}//skip the source network if ( network.isFlagged(nodeToCheck) ) { //it's flagged in another network, so flag it in the source source.setFlagged(nodeToCheck, true); break; //no point in checking other networks } } } } //the event can reference both restored nodes and edges if (event.isEdgesRestoredType()) {//edges added to a graph Edge[] newEdges = event.getRestoredEdges();//all restored edges for (int n = 0; n < newEdges.length; n++) { Edge edgeToCheck = newEdges[n]; //see if any linked network currently flags this edge for (Iterator iter = Cytoscape.getNetworkSet().iterator(); iter.hasNext(); ) { CyNetwork network = (CyNetwork) iter.next(); if (network == source) {continue;}//skip the source network if ( network.isFlagged(edgeToCheck) ) { //it's flagged in another network, so flag it in the source source.setFlagged(edgeToCheck, true); break; //no point in checking other networks } } } } working = false;//listen to flag events again } /** * Menu item that activates this plugin. */ private class ActivateMultiNetworkNodeSelectionAction extends CytoscapeAction { public ActivateMultiNetworkNodeSelectionAction() {super("Activate Multi-Network Selection");} public void actionPerformed(ActionEvent ae) {activateNetworkLinker();} } /** * Menu item that deactivates this plugin. */ private class DeactivateMultiNetworkNodeSelectionAction extends CytoscapeAction { public DeactivateMultiNetworkNodeSelectionAction() {super("Deactivate Multi-Network Selection");} public void actionPerformed(ActionEvent ae) {deactivateNetworkLinker();} } }