/* Copyright 2008-2010 Gephi Authors : Mathieu Bastian <mathieu.bastian@gephi.org> Website : http://www.gephi.org This file is part of Gephi. Gephi is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Gephi 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Gephi. If not, see <http://www.gnu.org/licenses/>. */ package org.gephi.graph.dhns.core; import java.util.ArrayList; import java.util.List; import org.gephi.graph.api.GraphEvent.EventType; import org.gephi.graph.api.Node; import org.gephi.graph.dhns.edge.AbstractEdge; import org.gephi.graph.dhns.edge.MetaEdgeImpl; import org.gephi.graph.dhns.event.EdgeEvent; import org.gephi.graph.dhns.event.GeneralEvent; import org.gephi.graph.dhns.event.NodeEvent; import org.gephi.graph.dhns.node.AbstractNode; import org.gephi.graph.dhns.node.iterators.AbstractNodeIterator; import org.gephi.graph.dhns.node.iterators.ChildrenIterator; import org.gephi.graph.dhns.node.iterators.DescendantAndSelfIterator; import org.gephi.graph.dhns.node.iterators.DescendantIterator; import org.gephi.graph.dhns.node.iterators.TreeIterator; import org.gephi.graph.dhns.node.iterators.TreeListIterator; import org.gephi.graph.dhns.predicate.Tautology; /** * Business class for external operations on the data structure. Propose blocking mechanism. * * @author Mathieu Bastian */ public class StructureModifier { private final Dhns dhns; private final GraphVersion graphVersion; private final TreeStructure treeStructure; private final GraphViewImpl view; private final EdgeProcessor edgeProcessor; //Business private final Business business; public StructureModifier(Dhns dhns, GraphViewImpl view) { this.dhns = dhns; this.view = view; this.treeStructure = view.getStructure(); this.graphVersion = dhns.getGraphVersion(); edgeProcessor = new EdgeProcessor(dhns, view); business = new Business(); } public EdgeProcessor getEdgeProcessor() { return edgeProcessor; } public void expand(AbstractNode node) { dhns.writeLock(); if (node.level < treeStructure.getTreeHeight()) { business.expand(node); } graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new NodeEvent(EventType.EXPAND, node, view)); } public void retract(AbstractNode node) { dhns.writeLock(); if (node.level < treeStructure.getTreeHeight()) { business.retract(node); } graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new NodeEvent(EventType.RETRACT, node, view)); } public void addNode(AbstractNode node, AbstractNode parent) { dhns.writeLock(); AbstractNode parentNode; if (parent == null) { parentNode = treeStructure.getRoot(); } else { parentNode = (parent); } node.parent = parentNode; business.addNode(node); dhns.getGraphStructure().addToDictionnary(node); graphVersion.incNodeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new NodeEvent(EventType.ADD_NODES, node, view)); } public void deleteNode(AbstractNode node) { if (view.isMainView() && node.getNodeData().getNodes().getCount() > 1) { dhns.writeLock(); for (AbstractNodeIterator itr = node.getNodeData().getNodes().iterator(); itr.hasNext();) { AbstractNode nodeInOtherView = itr.next(); if (nodeInOtherView.getViewId() != view.getViewId()) { GraphViewImpl otherView = nodeInOtherView.avlNode.getList().getView(); business.deleteNode(nodeInOtherView, otherView); } } AbstractNode[] deletesNodes = business.deleteNode(node, view); graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); for (int i = 0; i < deletesNodes.length; i++) { dhns.getEventManager().fireEvent(new NodeEvent(EventType.REMOVE_NODES, deletesNodes[i], view)); } } else { dhns.writeLock(); AbstractNode[] deletesNodes = business.deleteNode(node, view); graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); for (int i = 0; i < deletesNodes.length; i++) { dhns.getEventManager().fireEvent(new NodeEvent(EventType.REMOVE_NODES, deletesNodes[i], view)); } } } public void addEdge(AbstractEdge edge) { dhns.writeLock(); business.addEdge(edge); graphVersion.incEdgeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new EdgeEvent(EventType.ADD_EDGES, edge, view)); } public boolean deleteEdge(AbstractEdge edge) { dhns.writeLock(); boolean res = business.delEdge(edge); graphVersion.incEdgeVersion(); dhns.writeUnlock(); if (res) { dhns.getEventManager().fireEvent(new EdgeEvent(EventType.REMOVE_EDGES, edge, view)); } return res; } public boolean deleteMetaEdge(AbstractEdge edge) { dhns.writeLock(); boolean res = business.delMetaEdge((MetaEdgeImpl) edge); graphVersion.incEdgeVersion(); dhns.writeUnlock(); return res; } public void clear() { dhns.writeLock(); AbstractEdge[] clearedEdges = business.clearAllEdges(); AbstractNode[] clearedNodes = business.clearAllNodes(); graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); if (clearedEdges != null) { for (int i = 0; i < clearedEdges.length; i++) { if (clearedEdges[i] != null) { dhns.getEventManager().fireEvent(new EdgeEvent(EventType.REMOVE_EDGES, clearedEdges[i], view)); } } } if (clearedNodes != null) { for (int i = 0; i < clearedNodes.length; i++) { if (clearedNodes[i] != null) { dhns.getEventManager().fireEvent(new NodeEvent(EventType.REMOVE_NODES, clearedNodes[i], view)); } } } } public void clearEdges() { dhns.writeLock(); AbstractEdge[] clearedEdges = business.clearAllEdges(); graphVersion.incEdgeVersion(); dhns.writeUnlock(); if (clearedEdges != null) { for (int i = 0; i < clearedEdges.length; i++) { if (clearedEdges[i] != null) { dhns.getEventManager().fireEvent(new EdgeEvent(EventType.REMOVE_EDGES, clearedEdges[i], view)); } } } } public void clearEdges(AbstractNode node) { dhns.writeLock(); AbstractEdge[] clearedEdges = business.clearEdges(node); graphVersion.incEdgeVersion(); dhns.writeUnlock(); if (clearedEdges != null) { for (int i = 0; i < clearedEdges.length; i++) { if (clearedEdges[i] != null) { dhns.getGraphStructure().removeFromDictionnary(clearedEdges[i]); dhns.getEventManager().fireEvent(new EdgeEvent(EventType.REMOVE_EDGES, clearedEdges[i], view)); } } } } public void clearMetaEdges(AbstractNode node) { dhns.writeLock(); business.clearMetaEdges(node); graphVersion.incEdgeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new GeneralEvent(EventType.META_EDGES_UPDATE, view)); } public void resetViewToLeaves() { dhns.writeLock(); edgeProcessor.clearAllMetaEdges(); view.setNodesEnabled(0); for (TreeListIterator itr = new TreeListIterator(treeStructure.getTree(), 1); itr.hasNext();) { AbstractNode node = itr.next(); node.setEnabled(node.size == 0); if (node.isEnabled()) { view.incNodesEnabled(1); } edgeProcessor.resetEdgesCounting(node); } view.setEdgesCountEnabled(0); view.setMutualEdgesEnabled(0); for (TreeIterator itr = new TreeIterator(treeStructure, true, Tautology.instance); itr.hasNext();) { AbstractNode node = itr.next(); edgeProcessor.computeMetaEdges(node, node); edgeProcessor.computeEdgesCounting(node); } graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new GeneralEvent(EventType.META_EDGES_UPDATE, view)); } public void resetViewToTopNodes() { dhns.writeLock(); edgeProcessor.clearAllMetaEdges(); view.setNodesEnabled(0); for (TreeListIterator itr = new TreeListIterator(treeStructure.getTree(), 1); itr.hasNext();) { AbstractNode node = itr.next(); node.setEnabled(node.parent == treeStructure.root); if (node.isEnabled()) { view.incNodesEnabled(1); } edgeProcessor.resetEdgesCounting(node); } view.setEdgesCountEnabled(0); view.setMutualEdgesEnabled(0); for (TreeIterator itr = new TreeIterator(treeStructure, true, Tautology.instance); itr.hasNext();) { AbstractNode node = itr.next(); edgeProcessor.computeMetaEdges(node, node); edgeProcessor.computeEdgesCounting(node); } graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new GeneralEvent(EventType.META_EDGES_UPDATE, view)); } public void resetViewToLevel(int level) { dhns.writeLock(); edgeProcessor.clearAllMetaEdges(); view.setNodesEnabled(0); for (TreeListIterator itr = new TreeListIterator(treeStructure.getTree(), 1); itr.hasNext();) { AbstractNode node = itr.next(); node.setEnabled(node.level == level); if (node.isEnabled()) { view.incNodesEnabled(1); } edgeProcessor.resetEdgesCounting(node); } view.setEdgesCountEnabled(0); view.setMutualEdgesEnabled(0); for (TreeIterator itr = new TreeIterator(treeStructure, true, Tautology.instance); itr.hasNext();) { AbstractNode node = itr.next(); edgeProcessor.computeMetaEdges(node, node); edgeProcessor.computeEdgesCounting(node); } graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new GeneralEvent(EventType.META_EDGES_UPDATE, view)); } public void moveToGroup(AbstractNode node, AbstractNode nodeGroup) { dhns.writeLock(); business.moveToGroup(node, nodeGroup); graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new NodeEvent(EventType.MOVE_NODES, node, view)); } public Node group(AbstractNode[] nodes) { dhns.writeLock(); AbstractNode group = dhns.factory().newNode(view.getViewId()); business.group(group, nodes); graphVersion.incNodeAndEdgeVersion(); dhns.getGraphStructure().addToDictionnary(group); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new NodeEvent(EventType.ADD_NODES, group, view)); for (int i = 0; i < nodes.length; i++) { dhns.getEventManager().fireEvent(new NodeEvent(EventType.MOVE_NODES, nodes[i], view)); } return group; } public void ungroup(AbstractNode nodeGroup) { dhns.writeLock(); AbstractNode[] ungroupedNodes = business.ungroup(nodeGroup); graphVersion.incNodeAndEdgeVersion(); dhns.writeUnlock(); dhns.getEventManager().fireEvent(new NodeEvent(EventType.REMOVE_NODES, nodeGroup, view)); for (int i = 0; i < ungroupedNodes.length; i++) { dhns.getEventManager().fireEvent(new NodeEvent(EventType.MOVE_NODES, ungroupedNodes[i], view)); } } public void flatten() { dhns.writeLock(); if (treeStructure.getTreeHeight() > 1) { TreeIterator nodesIterator = new TreeIterator(treeStructure, true, Tautology.instance); for (; nodesIterator.hasNext();) { AbstractNode node = nodesIterator.next(); AbstractEdge[] newEdges = edgeProcessor.flattenNode(node); if (newEdges != null) { for (int i = 0; i < newEdges.length; i++) { AbstractEdge e = newEdges[i]; if (e != null) { dhns.getGraphStructure().addToDictionnary(e); dhns.getEventManager().fireEvent(new EdgeEvent(EventType.ADD_EDGES, e, view)); } } } } List<AbstractNode> nodesToDelete = new ArrayList<AbstractNode>(); List<AbstractNode> nodesToKeep = new ArrayList<AbstractNode>(); for (TreeListIterator itr = new TreeListIterator(treeStructure.getTree(), 1); itr.hasNext();) { AbstractNode node = itr.next(); if (!node.isEnabled()) { nodesToDelete.add(node); } else { nodesToKeep.add(node); } } for (AbstractNode node : nodesToDelete) { //Del edges AbstractEdge[] deletedEdges = edgeProcessor.clearEdges(node); if (deletedEdges != null) { for (int j = 0; j < deletedEdges.length; j++) { if (deletedEdges[j] != null) { dhns.getGraphStructure().removeFromDictionnary(deletedEdges[j]); dhns.getEventManager().fireEvent(new EdgeEvent(EventType.REMOVE_EDGES, deletedEdges[j], view)); } } } dhns.getGraphStructure().removeFromDictionnary(node); treeStructure.deleteOnlySelf(node); dhns.getEventManager().fireEvent(new NodeEvent(EventType.REMOVE_NODES, node, view)); } for (AbstractNode node : nodesToKeep) { node.size = 0; node.parent = treeStructure.root; node.level = 1; node.getPost(); } treeStructure.root.size = nodesToKeep.size(); treeStructure.root.getPost(); treeStructure.resetLevelSize(nodesToKeep.size()); graphVersion.incNodeAndEdgeVersion(); } dhns.writeUnlock(); dhns.getEventManager().fireEvent(new GeneralEvent(EventType.META_EDGES_UPDATE, view)); } //------------------------------------------ private class Business { private void expand(AbstractNode absNode) { //Disable parent absNode.setEnabled(false); view.decNodesEnabled(1); edgeProcessor.clearMetaEdges(absNode); //Enable children for (ChildrenIterator itr = new ChildrenIterator(treeStructure, absNode, Tautology.instance); itr.hasNext();) { AbstractNode child = itr.next(); child.setEnabled(true); view.incNodesEnabled(1); edgeProcessor.computeMetaEdges(child, child); } //Update counting for (ChildrenIterator itr = new ChildrenIterator(treeStructure, absNode, Tautology.instance); itr.hasNext();) { AbstractNode child = itr.next(); edgeProcessor.incrementEdgesCounting(child, absNode); } edgeProcessor.decrementEdgesCouting(absNode, null); } private void retract(AbstractNode parent) { //Disable children for (ChildrenIterator itr = new ChildrenIterator(treeStructure, parent, Tautology.instance); itr.hasNext();) { AbstractNode child = itr.next(); child.setEnabled(false); view.decNodesEnabled(1); edgeProcessor.clearMetaEdges(child); } //Enable node parent.setEnabled(true); view.incNodesEnabled(1); edgeProcessor.computeMetaEdges(parent, parent); //Edges counting for (ChildrenIterator itr = new ChildrenIterator(treeStructure, parent, Tautology.instance); itr.hasNext();) { AbstractNode child = itr.next(); edgeProcessor.decrementEdgesCouting(child, parent); } edgeProcessor.incrementEdgesCounting(parent, null); } private void addNode(AbstractNode node) { boolean enabled = (treeStructure.getEnabledAncestor(node) == null); node.setEnabled(enabled); treeStructure.insertAsChild(node, node.parent); if (node.isEnabled()) { view.incNodesEnabled(1); } } private void addEdge(AbstractEdge edge) { AbstractNode sourceNode = edge.getSource(view.getViewId()); AbstractNode targetNode = edge.getTarget(view.getViewId()); boolean enabled = sourceNode.isEnabled() && targetNode.isEnabled(); //Add Edges sourceNode.getEdgesOutTree().add(edge); targetNode.getEdgesInTree().add(edge); if (!edge.isSelfLoop() && sourceNode.getEdgesInTree().hasNeighbour(targetNode)) { //Mututal edge view.incMutualEdgesTotal(1); if (enabled) { sourceNode.incEnabledMutualDegree(); targetNode.incEnabledMutualDegree(); view.incMutualEdgesEnabled(1); } } view.incEdgesCountTotal(1); if (enabled) { view.incEdgesCountEnabled(1); sourceNode.incEnabledOutDegree(); targetNode.incEnabledInDegree(); } dhns.getGraphStructure().addToDictionnary(edge); //Add Meta Edge if (!edge.isSelfLoop()) { edgeProcessor.createMetaEdge(edge); } } private AbstractNode[] deleteNode(AbstractNode node, GraphViewImpl graphView) { AbstractNode[] descendants = new AbstractNode[node.size + 1]; int i = 0; for (DescendantAndSelfIterator itr = new DescendantAndSelfIterator(graphView.getStructure(), node, Tautology.instance); itr.hasNext();) { AbstractNode descendant = itr.next(); descendants[i] = descendant; if (descendant.isEnabled()) { graphView.getStructureModifier().edgeProcessor.clearMetaEdges(descendant); graphView.decNodesEnabled(1); } AbstractEdge[] deletedEdges = graphView.getStructureModifier().edgeProcessor.clearEdges(descendant); if (deletedEdges != null) { for (int j = 0; j < deletedEdges.length; j++) { if (deletedEdges[j] != null) { dhns.getGraphStructure().removeFromDictionnary(deletedEdges[j]); dhns.getEventManager().fireEvent(new EdgeEvent(EventType.REMOVE_EDGES, deletedEdges[j], graphView)); } } } dhns.getGraphStructure().removeFromDictionnary(descendant); i++; } graphView.getStructure().deleteDescendantAndSelf(node); return descendants; } private boolean delEdge(AbstractEdge edge) { AbstractNode source = edge.getSource(view.getViewId()); AbstractNode target = edge.getTarget(view.getViewId()); boolean enabled = source.isEnabled() && target.isEnabled(); if (!edge.isSelfLoop() && source.getEdgesInTree().hasNeighbour(target)) { //mutual if (enabled) { view.decMutualEdgesEnabled(1); source.decEnabledMutualDegree(); target.decEnabledMutualDegree(); } view.decMutualEdgesTotal(1); } view.decEdgesCountTotal(1); if (enabled) { view.decEdgesCountEnabled(1); source.decEnabledOutDegree(); target.decEnabledInDegree(); } //Remove edge boolean res = source.getEdgesOutTree().remove(edge); res = res && target.getEdgesInTree().remove(edge); dhns.getGraphStructure().removeFromDictionnary(edge); //Remove edge from possible metaEdge edgeProcessor.removeEdgeFromMetaEdge(edge); return res; } public boolean delMetaEdge(MetaEdgeImpl edge) { AbstractNode source = edge.getSource(view.getViewId()); AbstractNode target = edge.getTarget(view.getViewId()); if (!edge.isSelfLoop() && source.getEdgesInTree().hasNeighbour(target)) { //mutual view.decMutualMetaEdgesTotal(1); source.decMutualMetaEdgeDegree(); target.decMutualMetaEdgeDegree(); } view.decMetaEdgesCount(1); //Remove edge boolean res = source.getMetaEdgesOutTree().remove(edge); res = res && target.getMetaEdgesInTree().remove(edge); return res; } private AbstractEdge[] clearAllEdges() { return edgeProcessor.clearAllEdges(); } private AbstractNode[] clearAllNodes() { AbstractNode[] deletedNodes = new AbstractNode[treeStructure.getTreeSize() - 1]; int n = 0; for (TreeListIterator itr = new TreeListIterator(treeStructure.getTree(), 1); itr.hasNext();) { AbstractNode node = itr.next(); node.getNodeData().getNodes().remove(view.getViewId()); dhns.getGraphStructure().removeFromDictionnary(node); deletedNodes[n++] = node; } treeStructure.clear(); view.setNodesEnabled(0); return deletedNodes; } private AbstractEdge[] clearEdges(AbstractNode node) { return edgeProcessor.clearEdges(node); } private void clearMetaEdges(AbstractNode node) { edgeProcessor.clearMetaEdges(node); } private void group(AbstractNode group, AbstractNode[] nodes) { group.setEnabled(true); AbstractNode parent = ((AbstractNode) nodes[0]).parent; //parent = parent.getInView(view.getViewId()); group.parent = parent; business.addNode(group); for (int i = 0; i < nodes.length; i++) { AbstractNode nodeToGroup = nodes[i]; nodeToGroup = nodeToGroup.getInView(view.getViewId()); business.moveToGroup(nodeToGroup, group); } } private AbstractNode[] ungroup(AbstractNode nodeGroup) { //TODO Better implementation. Just remove nodeGroup from the treelist and lower level of children int count = 0; for (ChildrenIterator itr = new ChildrenIterator(treeStructure, nodeGroup, Tautology.instance); itr.hasNext();) { itr.next(); count++; } AbstractNode[] ungroupedNodes = new AbstractNode[count]; if (nodeGroup.isEnabled()) { business.expand(nodeGroup); } for (int i = 0; i < count; i++) { AbstractNode node = treeStructure.getNodeAt(nodeGroup.getPre() + 1); business.moveToGroup(node, nodeGroup.parent); ungroupedNodes[i] = node; } business.deleteNode(nodeGroup, view); return ungroupedNodes; } private void moveToGroup(AbstractNode node, AbstractNode nodeGroup) { AbstractNode toMoveAncestor = treeStructure.getEnabledAncestor(node); AbstractNode destinationAncestor = treeStructure.getEnabledAncestorOrSelf(nodeGroup); if (toMoveAncestor != destinationAncestor) { if (toMoveAncestor != null) { //The node has an enabled ancestor //We delete edges from potential meta edges if (node.size > 0) { for (DescendantAndSelfIterator itr = new DescendantAndSelfIterator(treeStructure, node, Tautology.instance); itr.hasNext();) { AbstractNode descendant = itr.next(); edgeProcessor.clearEdgesWithoutRemove(descendant); } } else { edgeProcessor.clearEdgesWithoutRemove(node); } } else if (node.isEnabled()) { //The node is enabled if (destinationAncestor != null) { //The destination is enabled or has enabled ancestor //Node is thus disabled edgeProcessor.clearMetaEdges(node); node.setEnabled(false); view.decNodesEnabled(1); edgeProcessor.decrementEdgesCouting(node, null); //DO } else { //The node is kept enabled //Meta edges are still valid only if their target is out of the dest cluster edgeProcessor.clearMetaEdgesOutOfRange(node, nodeGroup); } } else if (node.size > 0) { if (destinationAncestor != null) { //The node may have some enabled descendants and we set them disabled for (DescendantIterator itr = new DescendantIterator(treeStructure, node, Tautology.instance); itr.hasNext();) { AbstractNode descendant = itr.next(); if (descendant.isEnabled()) { edgeProcessor.clearMetaEdges(descendant); descendant.setEnabled(false); view.decNodesEnabled(1); edgeProcessor.decrementEdgesCouting(descendant, null); //TODO } } //DO } else { //The node may have some enabled descendants and we keep them enabled for (DescendantIterator itr = new DescendantIterator(treeStructure, node, Tautology.instance); itr.hasNext();) { AbstractNode descendant = itr.next(); if (descendant.isEnabled()) { //Enabled descendants meta edges are still valid only if their target is out of //the destination cluster edgeProcessor.clearMetaEdgesOutOfRange(node, nodeGroup); } } } } } treeStructure.move(node, nodeGroup); if (destinationAncestor != null) { destinationAncestor.getPre(); //Compute all meta edges for the descendants of node and append them to the enabled //destinationAncestor edgeProcessor.computeMetaEdges(node, destinationAncestor); } } /* private void cloneDescedantAndSelft(AbstractNode node, AbstractNode parentNode) { if (node.size > 0) { DescendantAndSelfIterator itr = new DescendantAndSelfIterator(treeStructure, node, Tautology.instance); for (; itr.hasNext();) { AbstractNode desc = itr.next(); CloneNode clone = new CloneNode(desc); if (desc == node) { //Parent is the given parentNode clone.parent = parentNode; } else { clone.parent = desc.parent.getOriginalNode().getClones(); //The last clone added } addNode(clone); } } else { CloneNode clone = new CloneNode(node); clone.parent = parentNode; addNode(clone); } } private void unAttachClones(CloneNode node) { for (int i = node.getPre(); i <= node.pre + node.size; i++) { AbstractNode n = treeStructure.getNodeAt(i); if (n.isClone()) { n.getOriginalNode().removeClone((CloneNode) n); } } }*/ } }