/*
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.partition.impl;
import java.util.ArrayList;
import java.util.List;
import org.gephi.data.attributes.api.AttributeEvent;
import org.gephi.data.attributes.api.AttributeTable;
import org.gephi.data.attributes.api.AttributeColumn;
import org.gephi.data.attributes.api.AttributeController;
import org.gephi.data.attributes.api.AttributeListener;
import org.gephi.data.attributes.api.AttributeModel;
import org.gephi.data.attributes.api.AttributeUtils;
import org.gephi.data.attributes.api.Estimator;
import org.gephi.data.attributes.type.TimeInterval;
import org.gephi.dynamic.api.DynamicModel;
import org.gephi.graph.api.Graph;
import org.gephi.graph.api.GraphController;
import org.gephi.graph.api.GraphEvent;
import org.gephi.graph.api.GraphListener;
import org.gephi.graph.api.GraphModel;
import org.gephi.graph.api.HierarchicalGraph;
import org.gephi.graph.api.Node;
import org.gephi.partition.api.EdgePartition;
import org.gephi.partition.api.NodePartition;
import org.gephi.partition.api.Part;
import org.gephi.partition.api.Partition;
import org.gephi.partition.api.PartitionController;
import org.gephi.partition.api.PartitionModel;
import org.gephi.partition.spi.Transformer;
import org.gephi.partition.spi.TransformerBuilder;
import org.gephi.project.api.ProjectController;
import org.gephi.project.api.Workspace;
import org.gephi.project.api.WorkspaceListener;
import org.openide.util.Lookup;
import org.openide.util.lookup.ServiceProvider;
/**
*
* @author Mathieu Bastian
*/
@ServiceProvider(service = PartitionController.class)
public class PartitionControllerImpl implements PartitionController, AttributeListener {
private PartitionModelImpl model;
private boolean refreshPartitions = true;
public PartitionControllerImpl() {
ProjectController pc = Lookup.getDefault().lookup(ProjectController.class);
pc.addWorkspaceListener(new WorkspaceListener() {
public void initialize(Workspace workspace) {
workspace.add(new PartitionModelImpl());
}
public void select(Workspace workspace) {
model = workspace.getLookup().lookup(PartitionModelImpl.class);
if (model == null) {
model = new PartitionModelImpl();
workspace.add(model);
}
refreshPartitions = true;
GraphModel gm = Lookup.getDefault().lookup(GraphController.class).getModel(workspace);
trachViewChange(gm);
AttributeModel attributeModel = Lookup.getDefault().lookup(AttributeController.class).getModel(workspace);
attributeModel.addAttributeListener(PartitionControllerImpl.this);
}
public void unselect(Workspace workspace) {
GraphModel gm = Lookup.getDefault().lookup(GraphController.class).getModel(workspace);
untrackViewChange(gm);
model = null;
AttributeModel attributeModel = workspace.getLookup().lookup(AttributeModel.class);
attributeModel.removeAttributeListener(PartitionControllerImpl.this);
}
public void close(Workspace workspace) {
}
public void disable() {
untrackViewChange(null);
}
});
if (pc.getCurrentWorkspace() != null) {
refreshPartitions = true;
model = pc.getCurrentWorkspace().getLookup().lookup(PartitionModelImpl.class);
if (model == null) {
model = new PartitionModelImpl();
Workspace workspace = pc.getCurrentWorkspace();
pc.getCurrentWorkspace().add(model);
GraphModel gm = Lookup.getDefault().lookup(GraphController.class).getModel(workspace);
trachViewChange(gm);
AttributeModel attributeModel = Lookup.getDefault().lookup(AttributeController.class).getModel(workspace);
attributeModel.addAttributeListener(PartitionControllerImpl.this);
}
}
}
private GraphListener graphListener;
private void trachViewChange(final GraphModel graphModel) {
untrackViewChange(graphModel);
if (model.getVisibleViewId() == -1) {
model.setVisibleViewId(graphModel.getVisibleView().getViewId());
}
graphListener = new GraphListener() {
public void graphChanged(GraphEvent event) {
if (event.is(GraphEvent.EventType.VISIBLE_VIEW)) {
if (model.getVisibleViewId() != graphModel.getVisibleView().getViewId()) {
//View has been updated
model.setVisibleViewId(graphModel.getVisibleView().getViewId());
setSelectedPartition(null);
}
}
}
};
graphModel.addGraphListener(graphListener);
}
private void untrackViewChange(GraphModel graphModel) {
if (graphListener != null && graphModel != null) {
graphModel.removeGraphListener(graphListener);
}
graphListener = null;
}
public void attributesChanged(AttributeEvent event) {
refreshPartitions = true;
}
public void setSelectedPartition(final Partition partition) {
if (partition == model.getSelectedPartition()) {
return;
}
model.setWaiting(true);
if (model.getSelectedPartitioning() == PartitionModel.NODE_PARTITIONING) {
Thread t = new Thread(new Runnable() {
public void run() {
if (partition != null) {
GraphModel graphModel = Lookup.getDefault().lookup(GraphController.class).getModel();
DynamicModel dynamicModel = model.getDynamicModel();
TimeInterval timeInterval = dynamicModel != null ? dynamicModel.getVisibleInterval() : null;
Estimator estimator = AttributeUtils.getDefault().isDynamicNumberColumn(partition.getColumn()) ? model.getNumberEstimator() : model.getEstimator();
PartitionFactory.buildNodePartition((NodePartition) partition, graphModel.getGraphVisible(), timeInterval, estimator);
}
model.setNodePartition(partition);
if (model.getNodeTransformerBuilder() == null) {
//Select the first transformer
TransformerBuilder[] builders = Lookup.getDefault().lookupAll(TransformerBuilder.class).toArray(new TransformerBuilder[0]);
for (int i = 0; i < builders.length; i++) {
TransformerBuilder t = builders[i];
if (t instanceof TransformerBuilder.Node) {
model.setNodeBuilder(t);
break;
}
}
}
model.setWaiting(false);
}
}, "Partition Model refresh");
t.start();
} else {
Thread t = new Thread(new Runnable() {
public void run() {
if (partition != null) {
GraphModel graphModel = Lookup.getDefault().lookup(GraphController.class).getModel();
DynamicModel dynamicModel = model.getDynamicModel();
TimeInterval timeInterval = dynamicModel != null ? dynamicModel.getVisibleInterval() : null;
Estimator estimator = AttributeUtils.getDefault().isDynamicNumberColumn(partition.getColumn()) ? model.getNumberEstimator() : model.getEstimator();
PartitionFactory.buildEdgePartition((EdgePartition) partition, graphModel.getGraphVisible(), timeInterval, estimator);
}
model.setEdgePartition(partition);
if (model.getEdgeTransformerBuilder() == null) {
//Select the first transformer
TransformerBuilder[] builders = Lookup.getDefault().lookupAll(TransformerBuilder.class).toArray(new TransformerBuilder[0]);
for (int i = 0; i < builders.length; i++) {
TransformerBuilder t = builders[i];
if (t instanceof TransformerBuilder.Edge) {
model.setEdgeBuilder(t);
break;
}
}
}
model.setWaiting(false);
}
}, "Partition Model refresh");
t.start();
}
}
public Partition buildPartition(AttributeColumn column, Graph graph) {
DynamicModel dynamicModel = model.getDynamicModel();
TimeInterval timeInterval = dynamicModel != null ? dynamicModel.getVisibleInterval() : null;
if (AttributeUtils.getDefault().isNodeColumn(column)) {
NodePartition partition = PartitionFactory.createNodePartition(column);
Estimator estimator = AttributeUtils.getDefault().isDynamicNumberColumn(column) ? model.getNumberEstimator() : model.getEstimator();
PartitionFactory.buildNodePartition(partition, graph, timeInterval, estimator);
return partition;
} else {
EdgePartition partition = PartitionFactory.createEdgePartition(column);
Estimator estimator = AttributeUtils.getDefault().isDynamicNumberColumn(column) ? model.getNumberEstimator() : model.getEstimator();
PartitionFactory.buildEdgePartition(partition, graph, timeInterval, estimator);
return partition;
}
}
public void setSelectedPartitioning(final int partitioning) {
model.setWaiting(true);
Thread t = new Thread(new Runnable() {
public void run() {
model.setSelectedPartitioning(partitioning);
model.setWaiting(false);
}
}, "Partition Model refresh");
t.start();
}
public void setSelectedTransformerBuilder(final TransformerBuilder builder) {
model.setWaiting(true);
Thread t = new Thread(new Runnable() {
public void run() {
if (model.getSelectedPartitioning() == PartitionModel.NODE_PARTITIONING) {
model.setNodeBuilder(builder);
} else {
model.setEdgeBuilder(builder);
}
model.setWaiting(false);
}
}, "Partition Model refresh");
t.start();
}
public void refreshPartitions() {
if (refreshPartitions) {
refreshPartitions = false;
AttributeController ac = Lookup.getDefault().lookup(AttributeController.class);
GraphModel graphModel = Lookup.getDefault().lookup(GraphController.class).getModel();
//Nodes
List<NodePartition> nodePartitions = new ArrayList<NodePartition>();
AttributeTable nodeTable = ac.getModel().getNodeTable();
Graph graph = graphModel.getGraphVisible();
for (AttributeColumn column : nodeTable.getColumns()) {
if (PartitionFactory.isPartitionColumn(column) && PartitionFactory.isNodePartitionColumn(column, graph)) {
nodePartitions.add(PartitionFactory.createNodePartition(column));
} else if (PartitionFactory.isDynamicPartitionColumn(column)) {
DynamicModel dynamicModel = model.getDynamicModel();
TimeInterval timeInterval = dynamicModel != null ? dynamicModel.getVisibleInterval() : null;
Estimator estimator = AttributeUtils.getDefault().isDynamicNumberColumn(column) ? model.getNumberEstimator() : model.getEstimator();
if (PartitionFactory.isDynamicNodePartitionColumn(column, graph, timeInterval, estimator)) {
nodePartitions.add(PartitionFactory.createNodePartition(column));
}
}
}
model.setNodePartitions(nodePartitions.toArray(new NodePartition[0]));
//Edges
List<EdgePartition> edgePartitions = new ArrayList<EdgePartition>();
AttributeTable edgeClass = ac.getModel().getEdgeTable();
for (AttributeColumn column : edgeClass.getColumns()) {
if (PartitionFactory.isPartitionColumn(column) && PartitionFactory.isEdgePartitionColumn(column, graph)) {
edgePartitions.add(PartitionFactory.createEdgePartition(column));
} else if (PartitionFactory.isDynamicPartitionColumn(column)) {
DynamicModel dynamicModel = model.getDynamicModel();
TimeInterval timeInterval = dynamicModel != null ? dynamicModel.getVisibleInterval() : null;
Estimator estimator = AttributeUtils.getDefault().isDynamicNumberColumn(column) ? model.getNumberEstimator() : model.getEstimator();
if (PartitionFactory.isDynamicEdgePartitionColumn(column, graph, timeInterval, estimator)) {
edgePartitions.add(PartitionFactory.createEdgePartition(column));
}
}
}
model.setEdgePartitions(edgePartitions.toArray(new EdgePartition[0]));
}
}
public void transform(Partition partition, Transformer transformer) {
if (transformer != null && partition != null) {
transformer.transform(partition);
}
}
public boolean isGroupable(Partition partition) {
if (partition instanceof NodePartition) {
if (partition.getPartsCount() > 0) {
NodePartition nodePartition = (NodePartition) partition;
Node n0 = nodePartition.getParts()[0].getObjects()[0];
GraphModel graphModel = Lookup.getDefault().lookup(GraphController.class).getModel();
HierarchicalGraph graph = graphModel.getHierarchicalGraphVisible();
if (graph.contains(n0) && graph.getParent(n0) == null) {
return true;
}
}
}
return false;
}
public boolean isUngroupable(Partition partition) {
if (partition instanceof NodePartition) {
if (partition.getPartsCount() > 0) {
NodePartition nodePartition = (NodePartition) partition;
Node n0 = nodePartition.getParts()[0].getObjects()[0];
GraphModel graphModel = Lookup.getDefault().lookup(GraphController.class).getModel();
HierarchicalGraph graph = graphModel.getHierarchicalGraphVisible();
if (graph.contains(n0) && graph.getParent(n0) != null) {
return true;
}
}
}
return false;
}
public void group(Partition partition) {
NodePartition nodePartition = (NodePartition) partition;
GraphModel graphModel = Lookup.getDefault().lookup(GraphController.class).getModel();
HierarchicalGraph graph = graphModel.getHierarchicalGraphVisible();
for (Part<Node> p : nodePartition.getParts()) {
Node[] nodes = p.getObjects();
List<Node> validNodes = new ArrayList<Node>();
for (Node n : nodes) {
if (graph.contains(n)) {
validNodes.add(n);
}
}
if (!validNodes.isEmpty() && graph.getParent(validNodes.get(0)) == null) {
float centroidX = 0;
float centroidY = 0;
float sizes = 0;
float r = 0;
float g = 0;
float b = 0;
int len = 0;
for (Node n : validNodes) {
centroidX += n.getNodeData().x();
centroidY += n.getNodeData().y();
sizes += n.getNodeData().getSize();
r += n.getNodeData().r();
g += n.getNodeData().g();
b += n.getNodeData().b();
len++;
}
Node metaNode = graph.groupNodes(validNodes.toArray(new Node[0]));
metaNode.getNodeData().setX(centroidX / len);
metaNode.getNodeData().setY(centroidY / len);
metaNode.getNodeData().setLabel(p.getDisplayName());
metaNode.getNodeData().setSize(sizes / graph.getNodeCount() * 5f);
metaNode.getNodeData().setColor(r / len, g / len, b / len);
}
}
}
public void ungroup(Partition partition) {
NodePartition nodePartition = (NodePartition) partition;
GraphModel graphModel = Lookup.getDefault().lookup(GraphController.class).getModel();
HierarchicalGraph graph = graphModel.getHierarchicalGraphVisible();
for (Part<Node> p : nodePartition.getParts()) {
Node[] nodes = p.getObjects();
List<Node> validNodes = new ArrayList<Node>();
for (Node n : nodes) {
if (graph.contains(n)) {
validNodes.add(n);
}
}
if (!validNodes.isEmpty()) {
Node metaNode = graph.getParent(validNodes.get(0));
if (metaNode != null) {
graph.ungroupNodes(metaNode);
}
}
}
}
public void showPie(boolean showPie) {
model.setPie(showPie);
}
public PartitionModel getModel() {
return model;
}
}