/*
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.io.exporter.plugin;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import org.gephi.data.attributes.api.AttributeColumn;
import org.gephi.data.attributes.api.AttributeModel;
import org.gephi.data.attributes.api.AttributeOrigin;
import org.gephi.data.attributes.api.AttributeType;
import org.gephi.data.attributes.type.TimeInterval;
import org.gephi.dynamic.DynamicUtilities;
import org.gephi.dynamic.api.DynamicModel;
import org.gephi.graph.api.Edge;
import org.gephi.graph.api.EdgeIterable;
import org.gephi.graph.api.Graph;
import org.gephi.graph.api.GraphModel;
import org.gephi.graph.api.HierarchicalGraph;
import org.gephi.graph.api.Node;
import org.gephi.graph.api.NodeData;
import org.gephi.io.exporter.api.FileType;
import org.gephi.io.exporter.spi.GraphExporter;
import org.gephi.io.exporter.spi.CharacterExporter;
import org.gephi.project.api.Workspace;
import org.gephi.utils.longtask.spi.LongTask;
import org.gephi.utils.progress.Progress;
import org.gephi.utils.progress.ProgressTicket;
import org.openide.util.NbBundle;
/**
*
* @author Mathieu Bastian
*/
public class ExporterGDF implements GraphExporter, CharacterExporter, LongTask {
private Workspace workspace;
private boolean exportVisible;
private boolean cancel = false;
private ProgressTicket progressTicket;
//Settings
private boolean normalize = false;
private boolean simpleQuotes = false;
private boolean useQuotes = true;
private boolean exportColors = true;
private boolean exportPosition = true;
private boolean exportAttributes = true;
private boolean exportVisibility = false;
//Settings Helper
private float minSize;
private float maxSize;
private float minX;
private float maxX;
private float minY;
private float maxY;
private boolean edgeLabels;
private boolean edgeColors;
//Columns
private NodeColumnsGDF[] defaultNodeColumnsGDFs;
private EdgeColumnsGDF[] defaultEdgeColumnsGDFs;
private AttributeColumn[] nodeColumns;
private AttributeColumn[] edgeColumns;
//Buffer
private Writer writer;
//Dynamic
private TimeInterval visibleInterval;
public boolean execute() {
AttributeModel attributeModel = workspace.getLookup().lookup(AttributeModel.class);
GraphModel graphModel = workspace.getLookup().lookup(GraphModel.class);
Graph graph = null;
if (exportVisible) {
graph = graphModel.getGraphVisible();
} else {
graph = graphModel.getGraph();
}
DynamicModel dynamicModel = workspace.getLookup().lookup(DynamicModel.class);
visibleInterval = dynamicModel != null && exportVisible ? dynamicModel.getVisibleInterval() : new TimeInterval();
try {
exportData(graph, attributeModel);
} catch (Exception e) {
throw new RuntimeException(e);
}
return !cancel;
}
private void exportData(Graph graph, AttributeModel attributeModel) throws Exception {
Progress.start(progressTicket);
defaultNodeColumns(graph);
defaultEdgeColumns(graph);
attributesNodeColumns(attributeModel);
attributesEdgeColumns(attributeModel);
StringBuilder stringBuilder = new StringBuilder();
//Node intro
stringBuilder.append("nodedef> name VARCHAR,");
//Default Node columns title
for (NodeColumnsGDF c : defaultNodeColumnsGDFs) {
if (c.isEnable()) {
stringBuilder.append(c.getTitle());
stringBuilder.append(" ");
stringBuilder.append(c.getType().toString().toUpperCase());
if (c.getDefaultValue() != null) {
stringBuilder.append(" default ");
stringBuilder.append(c.getDefaultValue().toString());
}
stringBuilder.append(",");
}
}
//Attributes Node columns
for (AttributeColumn c : nodeColumns) {
if (!c.getOrigin().equals(AttributeOrigin.PROPERTY)) {
stringBuilder.append(c.getTitle());
stringBuilder.append(" ");
DataTypeGDF dataTypeGDF = getDataTypeGDF(c.getType());
stringBuilder.append(dataTypeGDF.toString().toUpperCase());
if (c.getDefaultValue() != null) {
stringBuilder.append(" default ");
stringBuilder.append(c.getDefaultValue().toString());
}
stringBuilder.append(",");
}
}
//Remove last coma
stringBuilder.setLength(stringBuilder.length() - 1);
stringBuilder.append("\n");
//Lock
graph.readLock();
//Options
if (normalize) {
calculateMinMax(graph);
}
//Calculate progress units count
int max = graph.getNodeCount() + graph.getEdgeCount();
Progress.switchToDeterminate(progressTicket, max);
//Node lines
for (Node node : graph.getNodes()) {
if (cancel) {
break;
}
NodeData nodeData = node.getNodeData();
//Id
stringBuilder.append(nodeData.getId());
stringBuilder.append(",");
//Default columns
for (NodeColumnsGDF c : defaultNodeColumnsGDFs) {
if (c.isEnable()) {
c.writeData(stringBuilder, node);
stringBuilder.append(",");
}
}
//Attributes columns
for (AttributeColumn c : nodeColumns) {
if (!c.getOrigin().equals(AttributeOrigin.PROPERTY)) {
Object val = node.getNodeData().getAttributes().getValue(c.getIndex());
val = DynamicUtilities.getDynamicValue(val, visibleInterval.getLow(), visibleInterval.getHigh());
if (val != null) {
if (c.getType().equals(AttributeType.STRING) || c.getType().equals(AttributeType.LIST_STRING)) {
String quote = !useQuotes ? "" : simpleQuotes ? "'" : "\"";
stringBuilder.append(quote);
stringBuilder.append(val.toString());
stringBuilder.append(quote);
} else {
stringBuilder.append(val.toString());
}
}
stringBuilder.append(",");
}
}
//Remove last coma
stringBuilder.setLength(stringBuilder.length() - 1);
stringBuilder.append("\n");
Progress.progress(progressTicket);
}
//Edge intro
stringBuilder.append("edgedef> node1,node2,");
//Edge settings helper
HierarchicalGraph hg = (HierarchicalGraph) graph;
for (Edge e : hg.getEdgesAndMetaEdges()) {
edgeColors = edgeColors || e.getEdgeData().r() != -1;
edgeLabels = edgeLabels || (e.getEdgeData().getLabel() != null && !e.getEdgeData().getLabel().isEmpty());
}
//Edge columns title
for (EdgeColumnsGDF c : defaultEdgeColumnsGDFs) {
if (c.isEnable()) {
stringBuilder.append(c.getTitle());
stringBuilder.append(" ");
stringBuilder.append(c.getType().toString().toUpperCase());
if (c.getDefaultValue() != null) {
stringBuilder.append(" default ");
stringBuilder.append(c.getDefaultValue().toString());
}
stringBuilder.append(",");
}
}
//Attributes Edge columns
for (AttributeColumn c : edgeColumns) {
if (!c.getOrigin().equals(AttributeOrigin.PROPERTY)) {
stringBuilder.append(c.getTitle());
stringBuilder.append(" ");
DataTypeGDF dataTypeGDF = getDataTypeGDF(c.getType());
stringBuilder.append(dataTypeGDF.toString().toUpperCase());
if (c.getDefaultValue() != null) {
stringBuilder.append(" default ");
stringBuilder.append(c.getDefaultValue().toString());
}
stringBuilder.append(",");
}
}
//Remove last coma
stringBuilder.setLength(stringBuilder.length() - 1);
stringBuilder.append("\n");
//MetaEdges
EdgeIterable edgeIterable;
if (graph.getGraphModel().isHierarchical()) {
HierarchicalGraph hierarchicalGraph = (HierarchicalGraph) graph;
edgeIterable = hierarchicalGraph.getEdgesAndMetaEdges();
} else {
edgeIterable = graph.getEdges();
}
//Edge lines
for (Edge edge : edgeIterable) {
if (cancel) {
break;
}
//Source & Target
stringBuilder.append(edge.getSource().getNodeData().getId());
stringBuilder.append(",");
stringBuilder.append(edge.getTarget().getNodeData().getId());
stringBuilder.append(",");
//Default columns
for (EdgeColumnsGDF c : defaultEdgeColumnsGDFs) {
if (c.isEnable()) {
c.writeData(stringBuilder, edge);
stringBuilder.append(",");
}
}
//Attributes columns
for (AttributeColumn c : edgeColumns) {
Object val = edge.getEdgeData().getAttributes().getValue(c.getIndex());
val = DynamicUtilities.getDynamicValue(val, visibleInterval.getLow(), visibleInterval.getHigh());
if (val != null) {
if (c.getType().equals(AttributeType.STRING) || c.getType().equals(AttributeType.LIST_STRING)) {
String quote = !useQuotes ? "" : simpleQuotes ? "'" : "\"";
stringBuilder.append(quote);
stringBuilder.append(val.toString());
stringBuilder.append(quote);
} else {
stringBuilder.append(val.toString());
}
}
stringBuilder.append(",");
}
//Remove last coma
stringBuilder.setLength(stringBuilder.length() - 1);
stringBuilder.append("\n");
Progress.progress(progressTicket);
}
//Unlock
graph.readUnlockAll();
//Write StringBuilder
if (!cancel) {
writer.append(stringBuilder);
}
Progress.finish(progressTicket);
}
private void attributesNodeColumns(AttributeModel attributeModel) {
List<AttributeColumn> cols = new ArrayList<AttributeColumn>();
if (attributeModel != null) {
for (AttributeColumn column : attributeModel.getNodeTable().getColumns()) {
if (!isNodeDefaultColumn(column.getId())) {
cols.add(column);
}
}
}
nodeColumns = cols.toArray(new AttributeColumn[0]);
}
private void attributesEdgeColumns(AttributeModel attributeModel) {
List<AttributeColumn> cols = new ArrayList<AttributeColumn>();
if (attributeModel != null) {
for (AttributeColumn column : attributeModel.getEdgeTable().getColumns()) {
if (!isEdgeDefaultColumn(column.getId())) {
cols.add(column);
}
}
}
edgeColumns = cols.toArray(new AttributeColumn[0]);
}
private boolean isNodeDefaultColumn(String id) {
for (NodeColumnsGDF c : defaultNodeColumnsGDFs) {
if (c.title.equalsIgnoreCase(id)) {
return true;
}
}
return false;
}
private boolean isEdgeDefaultColumn(String id) {
for (EdgeColumnsGDF c : defaultEdgeColumnsGDFs) {
if (c.title.equalsIgnoreCase(id)) {
return true;
}
}
return false;
}
private void defaultNodeColumns(Graph graph) {
NodeColumnsGDF labelColumn = new NodeColumnsGDF("label") {
@Override
public boolean isEnable() {
return true;
}
@Override
public void writeData(StringBuilder builder, Node node) {
String label = node.getNodeData().getLabel();
if (label != null) {
String quote = !useQuotes ? "" : simpleQuotes ? "'" : "\"";
builder.append(quote);
builder.append(label);
builder.append(quote);
}
}
};
NodeColumnsGDF visibleColumn = new NodeColumnsGDF("visible", DataTypeGDF.BOOLEAN) {
@Override
public boolean isEnable() {
return exportVisibility;
}
@Override
public void writeData(StringBuilder builder, Node node) {
builder.append(true);
}
};
NodeColumnsGDF labelVisibleColumn = new NodeColumnsGDF("labelvisible", DataTypeGDF.BOOLEAN) {
@Override
public boolean isEnable() {
return exportVisibility;
}
@Override
public void writeData(StringBuilder builder, Node node) {
builder.append(node.getNodeData().getTextData().isVisible());
}
};
NodeColumnsGDF widthColumn = new NodeColumnsGDF("width", DataTypeGDF.DOUBLE) {
@Override
public boolean isEnable() {
return exportPosition;
}
@Override
public void writeData(StringBuilder builder, Node node) {
float size = node.getNodeData().getSize();
if (normalize) {
size = (size - minSize) / (maxSize - minSize);
}
builder.append(size);
}
};
NodeColumnsGDF heightColumn = new NodeColumnsGDF("height", DataTypeGDF.DOUBLE) {
@Override
public boolean isEnable() {
return exportPosition;
}
@Override
public void writeData(StringBuilder builder, Node node) {
float size = node.getNodeData().getSize();
if (normalize) {
size = (size - minSize) / (maxSize - minSize);
}
builder.append(size);
}
};
NodeColumnsGDF xColumn = new NodeColumnsGDF("x", DataTypeGDF.DOUBLE) {
@Override
public boolean isEnable() {
return exportPosition;
}
@Override
public void writeData(StringBuilder builder, Node node) {
float x = node.getNodeData().x();
if (normalize && x != 0.0) {
x = (x - minX) / (maxX - minX);
}
builder.append(x);
}
};
NodeColumnsGDF yColumn = new NodeColumnsGDF("y", DataTypeGDF.DOUBLE) {
@Override
public boolean isEnable() {
return exportPosition;
}
@Override
public void writeData(StringBuilder builder, Node node) {
float y = node.getNodeData().y();
if (normalize && y != 0.0) {
y = (y - minY) / (maxY - minY);
}
builder.append(y);
}
};
NodeColumnsGDF colorColumn = new NodeColumnsGDF("color") {
@Override
public boolean isEnable() {
return exportColors;
}
@Override
public void writeData(StringBuilder builder, Node node) {
String quote = "'";
builder.append(quote);
builder.append((int) (node.getNodeData().r() * 255f));
builder.append(",");
builder.append((int) (node.getNodeData().g() * 255f));
builder.append(",");
builder.append((int) (node.getNodeData().b() * 255f));
builder.append(quote);
}
};
NodeColumnsGDF fixedColumn = new NodeColumnsGDF("fixed", DataTypeGDF.BOOLEAN) {
@Override
public boolean isEnable() {
return exportVisibility;
}
@Override
public void writeData(StringBuilder builder, Node node) {
builder.append(node.getNodeData().isFixed());
}
};
NodeColumnsGDF styleColumn = new NodeColumnsGDF("style", DataTypeGDF.INT) {
@Override
public boolean isEnable() {
return false;
}
@Override
public void writeData(StringBuilder builder, Node node) {
}
};
defaultNodeColumnsGDFs = new NodeColumnsGDF[10];
defaultNodeColumnsGDFs[0] = labelColumn;
defaultNodeColumnsGDFs[1] = visibleColumn;
defaultNodeColumnsGDFs[2] = labelVisibleColumn;
defaultNodeColumnsGDFs[3] = widthColumn;
defaultNodeColumnsGDFs[4] = heightColumn;
defaultNodeColumnsGDFs[5] = xColumn;
defaultNodeColumnsGDFs[6] = yColumn;
defaultNodeColumnsGDFs[7] = colorColumn;
defaultNodeColumnsGDFs[8] = fixedColumn;
defaultNodeColumnsGDFs[9] = styleColumn;
}
private void defaultEdgeColumns(final Graph graph) {
EdgeColumnsGDF labelColumn = new EdgeColumnsGDF("label") {
@Override
public boolean isEnable() {
return edgeLabels;
}
@Override
public void writeData(StringBuilder builder, Edge edge) {
String label = edge.getEdgeData().getLabel();
if (label != null) {
String quote = !useQuotes ? "" : simpleQuotes ? "'" : "\"";
builder.append(quote);
builder.append(label);
builder.append(quote);
}
}
};
EdgeColumnsGDF weightColumn = new EdgeColumnsGDF("weight", DataTypeGDF.DOUBLE) {
@Override
public boolean isEnable() {
return true;
}
@Override
public void writeData(StringBuilder builder, Edge edge) {
builder.append(edge.getWeight(visibleInterval.getLow(), visibleInterval.getHigh()));
}
};
EdgeColumnsGDF directedColumn = new EdgeColumnsGDF("directed", DataTypeGDF.BOOLEAN) {
@Override
public boolean isEnable() {
return true;
}
@Override
public void writeData(StringBuilder builder, Edge edge) {
builder.append(graph.isDirected(edge));
}
};
EdgeColumnsGDF colorColumn = new EdgeColumnsGDF("color") {
@Override
public boolean isEnable() {
return exportColors && edgeColors;
}
@Override
public void writeData(StringBuilder builder, Edge edge) {
if (edge.getEdgeData().r() != -1) {
String quote = "'";
builder.append(quote);
builder.append((int) (edge.getEdgeData().r() * 255f));
builder.append(",");
builder.append((int) (edge.getEdgeData().g() * 255f));
builder.append(",");
builder.append((int) (edge.getEdgeData().b() * 255f));
builder.append(quote);
}
}
};
EdgeColumnsGDF visibleColumn = new EdgeColumnsGDF("visible", DataTypeGDF.BOOLEAN) {
@Override
public boolean isEnable() {
return exportVisibility;
}
@Override
public void writeData(StringBuilder builder, Edge edge) {
builder.append(true);
}
};
EdgeColumnsGDF labelVisibleColumn = new EdgeColumnsGDF("labelvisible", DataTypeGDF.BOOLEAN) {
@Override
public boolean isEnable() {
return exportVisibility;
}
@Override
public void writeData(StringBuilder builder, Edge edge) {
builder.append(edge.getEdgeData().getTextData().isVisible());
}
};
EdgeColumnsGDF edgeIdColumn = new EdgeColumnsGDF("id", DataTypeGDF.VARCHAR) {
@Override
public boolean isEnable() {
return false;
}
@Override
public void writeData(StringBuilder builder, Edge edge) {
}
};
defaultEdgeColumnsGDFs = new EdgeColumnsGDF[7];
defaultEdgeColumnsGDFs[0] = edgeIdColumn;
defaultEdgeColumnsGDFs[1] = labelColumn;
defaultEdgeColumnsGDFs[2] = weightColumn;
defaultEdgeColumnsGDFs[3] = directedColumn;
defaultEdgeColumnsGDFs[4] = colorColumn;
defaultEdgeColumnsGDFs[5] = visibleColumn;
defaultEdgeColumnsGDFs[6] = labelVisibleColumn;
}
private void calculateMinMax(Graph graph) {
minX = Float.POSITIVE_INFINITY;
maxX = Float.NEGATIVE_INFINITY;
minY = Float.POSITIVE_INFINITY;
maxY = Float.NEGATIVE_INFINITY;
minSize = Float.POSITIVE_INFINITY;
maxSize = Float.NEGATIVE_INFINITY;
for (Node node : graph.getNodes()) {
NodeData nodeData = node.getNodeData();
minX = Math.min(minX, nodeData.x());
maxX = Math.max(maxX, nodeData.x());
minY = Math.min(minY, nodeData.y());
maxY = Math.max(maxY, nodeData.y());
minSize = Math.min(minSize, nodeData.getSize());
maxSize = Math.max(maxSize, nodeData.getSize());
}
}
public boolean cancel() {
cancel = true;
return true;
}
public void setProgressTicket(ProgressTicket progressTicket) {
this.progressTicket = progressTicket;
}
public String getName() {
return NbBundle.getMessage(getClass(), "ExporterGDF_name");
}
public FileType[] getFileTypes() {
FileType ft = new FileType(".gdf", NbBundle.getMessage(getClass(), "fileType_GDF_Name"));
return new FileType[]{ft};
}
public void setExportAttributes(boolean exportAttributes) {
this.exportAttributes = exportAttributes;
}
public void setExportColors(boolean exportColors) {
this.exportColors = exportColors;
}
public void setExportPosition(boolean exportPosition) {
this.exportPosition = exportPosition;
}
public void setNormalize(boolean normalize) {
this.normalize = normalize;
}
public void setSimpleQuotes(boolean simpleQuotes) {
this.simpleQuotes = simpleQuotes;
}
public boolean isExportAttributes() {
return exportAttributes;
}
public boolean isExportColors() {
return exportColors;
}
public boolean isExportPosition() {
return exportPosition;
}
public boolean isNormalize() {
return normalize;
}
public boolean isSimpleQuotes() {
return simpleQuotes;
}
public boolean isUseQuotes() {
return useQuotes;
}
public boolean isExportVisibility() {
return exportVisibility;
}
public void setExportVisibility(boolean exportVisibility) {
this.exportVisibility = exportVisibility;
}
public void setUseQuotes(boolean useQuotes) {
this.useQuotes = useQuotes;
}
private DataTypeGDF getDataTypeGDF(AttributeType type) {
switch (type) {
case BOOLEAN:
return DataTypeGDF.BOOLEAN;
case DOUBLE:
return DataTypeGDF.DOUBLE;
case FLOAT:
return DataTypeGDF.FLOAT;
case INT:
return DataTypeGDF.INTEGER;
case LONG:
return DataTypeGDF.INTEGER;
case STRING:
return DataTypeGDF.VARCHAR;
case DYNAMIC_BOOLEAN:
return DataTypeGDF.BOOLEAN;
case DYNAMIC_DOUBLE:
return DataTypeGDF.DOUBLE;
case DYNAMIC_FLOAT:
return DataTypeGDF.FLOAT;
case DYNAMIC_INT:
return DataTypeGDF.INTEGER;
case DYNAMIC_LONG:
return DataTypeGDF.INTEGER;
case DYNAMIC_STRING:
return DataTypeGDF.VARCHAR;
default:
return DataTypeGDF.VARCHAR;
}
}
private enum DataTypeGDF {
VARCHAR, BOOL, BOOLEAN, INTEGER, TINYINT, INT, DOUBLE, FLOAT
};
private abstract class NodeColumnsGDF {
protected final String title;
protected final DataTypeGDF type;
protected final Object defaultValue;
public NodeColumnsGDF(String title) {
this(title, DataTypeGDF.VARCHAR);
}
public NodeColumnsGDF(String title, DataTypeGDF type) {
this(title, type, null);
}
public NodeColumnsGDF(String title, DataTypeGDF type, Object defaultValue) {
this.title = title;
this.type = type;
this.defaultValue = defaultValue;
}
public abstract boolean isEnable();
public abstract void writeData(StringBuilder builder, Node node);
public String getTitle() {
return title;
}
public DataTypeGDF getType() {
return type;
}
public Object getDefaultValue() {
return defaultValue;
}
}
private abstract class EdgeColumnsGDF {
protected final String title;
protected final DataTypeGDF type;
protected final Object defaultValue;
public EdgeColumnsGDF(String title) {
this(title, DataTypeGDF.VARCHAR);
}
public EdgeColumnsGDF(String title, DataTypeGDF type) {
this(title, type, null);
}
public EdgeColumnsGDF(String title, DataTypeGDF type, Object defaultValue) {
this.title = title;
this.type = type;
this.defaultValue = defaultValue;
}
public abstract boolean isEnable();
public abstract void writeData(StringBuilder builder, Edge edge);
public String getTitle() {
return title;
}
public DataTypeGDF getType() {
return type;
}
public Object getDefaultValue() {
return defaultValue;
}
}
public boolean isExportVisible() {
return exportVisible;
}
public void setExportVisible(boolean exportVisible) {
this.exportVisible = exportVisible;
}
public void setWriter(Writer writer) {
this.writer = writer;
}
public Workspace getWorkspace() {
return workspace;
}
public void setWorkspace(Workspace workspace) {
this.workspace = workspace;
}
}