/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>,
Sebastien Heymann <sebastien.heymann@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.importer.plugin.file;
import java.io.BufferedReader;
import java.io.LineNumberReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.gephi.data.attributes.api.AttributeTable;
import org.gephi.data.attributes.api.AttributeColumn;
import org.gephi.data.attributes.api.AttributeType;
import org.gephi.io.importer.api.ContainerLoader;
import org.gephi.io.importer.api.EdgeDraft;
import org.gephi.io.importer.api.ImportUtils;
import org.gephi.io.importer.api.Issue;
import org.gephi.io.importer.api.NodeDraft;
import org.gephi.io.importer.api.Report;
import org.gephi.io.importer.spi.FileImporter;
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
* @author Sebastien Heymann
*/
public class ImporterGDF implements FileImporter, LongTask {
//Architecture
private Reader reader;
private ContainerLoader container;
private Report report;
private ProgressTicket progressTicket;
private boolean cancel = false;
//Extract
private List<String> nodeLines = new ArrayList<String>();
private List<String> edgeLines = new ArrayList<String>();
//Matcher
private String[] nodeLineStart;
private String[] edgeLineStart;
//Columns
private GDFColumn[] nodeColumns;
private GDFColumn[] edgeColumns;
public ImporterGDF() {
nodeLineStart = new String[]{"nodedef>name", "nodedef> name", "Nodedef>name", "Nodedef> name", "nodedef>\"name", "nodedef> \"name", "Nodedef>\"name", "Nodedef> \"name"};
edgeLineStart = new String[]{"edgedef>", "Edgedef>"};
}
public boolean execute(ContainerLoader container) {
this.container = container;
this.report = new Report();
LineNumberReader lineReader = ImportUtils.getTextReader(reader);
try {
importData(lineReader);
} catch (Exception e) {
throw new RuntimeException(e);
}
return !cancel;
}
private void importData(LineNumberReader reader) throws Exception {
Progress.start(progressTicket); //Progress
//Verify a node line exists and puts nodes and edges lines in arrays
walkFile(reader);
Progress.switchToDeterminate(progressTicket, nodeLines.size() + edgeLines.size()); //Progress
//Magix regex
Pattern pattern = Pattern.compile("(?<=(?:,|^)\")(.*?)(?=(?<=(?:[^\\\\]))\",|\"$)|(?<=(?:,|^)')(.*?)(?=(?<=(?:[^\\\\]))',|'$)|(?<=(?:,|^))(?=[^'\"])(.*?)(?=(?:,|$))|(?<=,)($)");
//Nodes
for (String nodeLine : nodeLines) {
if (cancel) {
return;
}
//Create Node
NodeDraft node = container.factory().newNodeDraft();
Matcher m = pattern.matcher(nodeLine);
int count = 0;
String id = "";
while (m.find()) {
int start = m.start();
int end = m.end();
if (start != end) {
String data = nodeLine.substring(start, end);
data = data.trim();
if (!data.isEmpty() && !data.toLowerCase().equals("null")) {
if (count == 0) {
//Id
id = data;
node.setId(data);
} else if (count - 1 < nodeColumns.length) {
if (nodeColumns[count - 1] != null) {
setNodeData(node, nodeColumns[count - 1], data);
}
} else {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat7", id), Issue.Level.SEVERE));
}
}
}
count++;
}
container.addNode(node);
Progress.progress(progressTicket); //Progress
}
//Edges
for (String edgeLine : edgeLines) {
if (cancel) {
return;
}
//Create Edge
EdgeDraft edge = container.factory().newEdgeDraft();
Matcher m = pattern.matcher(edgeLine);
int count = 0;
String id = "";
while (m.find()) {
int start = m.start();
int end = m.end();
if (start != end) {
String data = edgeLine.substring(start, end);
data = data.trim();
if (!data.isEmpty() && !data.toLowerCase().equals("null")) {
if (count == 0) {
NodeDraft nodeSource = container.getNode(data);
edge.setSource(nodeSource);
id = data;
} else if (count == 1) {
NodeDraft nodeTarget = container.getNode(data);
edge.setTarget(nodeTarget);
id += "," + data;
} else if (count - 2 < edgeColumns.length) {
if (edgeColumns[count - 2] != null) {
setEdgeData(edge, edgeColumns[count - 2], data);
}
} else {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat7", id), Issue.Level.SEVERE));
}
}
}
count++;
}
container.addEdge(edge);
Progress.progress(progressTicket); //Progress
}
}
private void walkFile(BufferedReader reader) throws Exception {
if (reader.ready()) {
String firstLine = reader.readLine();
if (isNodeFirstLine(firstLine)) {
findNodeColumns(firstLine);
boolean edgesWalking = false;
while (reader.ready() && !cancel) {
String line = reader.readLine();
if (isEdgeFirstLine(line)) {
edgesWalking = true;
findEdgeColumns(line);
} else {
if (!edgesWalking) {
//Nodes
nodeLines.add(line);
} else {
//Edges
edgeLines.add(line);
}
}
}
} else {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat1"), Issue.Level.CRITICAL));
}
} else {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat1"), Issue.Level.CRITICAL));
}
}
private void findNodeColumns(String line) throws Exception {
String[] columns = line.split(",");
nodeColumns = new GDFColumn[columns.length - 1];
for (int i = 1; i < columns.length; i++) {
String columnString = columns[i];
String typeString = "";
String columnName = "";
AttributeType type = AttributeType.STRING;
try {
typeString = columnString.substring(columnString.lastIndexOf(" ")).trim().toLowerCase();
} catch (IndexOutOfBoundsException e) {
}
try {
int end = columnString.lastIndexOf(" ");
if (end != -1) {
columnName = columnString.substring(0, end).trim().toLowerCase();
} else {
columnName = columnString.trim().toLowerCase();
}
} catch (IndexOutOfBoundsException e) {
}
//Check error
if (columnName.isEmpty()) {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat2"), Issue.Level.SEVERE));
columnName = "default" + i;
}
if (typeString.isEmpty()) {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat6", columnName), Issue.Level.INFO));
typeString = "varchar";
}
//Clean parenthesis
typeString = typeString.replaceAll("\\([0-9]*\\)", "");
if (typeString.equals("varchar")) {
type = AttributeType.STRING;
} else if (typeString.equals("bool")) {
type = AttributeType.BOOLEAN;
} else if (typeString.equals("boolean")) {
type = AttributeType.BOOLEAN;
} else if (typeString.equals("integer")) {
type = AttributeType.INT;
} else if (typeString.equals("tinyint")) {
type = AttributeType.INT;
} else if (typeString.equals("int")) {
type = AttributeType.INT;
} else if (typeString.equals("double")) {
type = AttributeType.DOUBLE;
} else if (typeString.equals("float")) {
type = AttributeType.FLOAT;
} else {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat5", typeString), Issue.Level.WARNING));
}
if (columnName.equals("x")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.X);
report.log("Node property found: x");
} else if (columnName.equals("y")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.Y);
report.log("Node property found: y");
} else if (columnName.equals("visible")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.VISIBLE);
report.log("Node property found: visible");
} else if (columnName.equals("color")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.COLOR);
report.log("Node property found: color");
} else if (columnName.equals("fixed")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.FIXED);
report.log("Node property found: fixed");
} else if (columnName.equals("style")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.STYLE);
report.log("Node property found: style");
} else if (columnName.equals("width")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.WIDTH);
report.log("Node property found: width");
} else if (columnName.equals("height")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.HEIGHT);
report.log("Node property found: height");
} else if (columnName.equals("label")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.LABEL);
report.log("Node property found: label");
} else if (columnName.equals("labelvisible")) {
nodeColumns[i - 1] = new GDFColumn(GDFColumn.NodeGuessColumn.LABELVISIBLE);
report.log("Node property found: labelvisible");
} else {
AttributeTable nodeClass = container.getAttributeModel().getNodeTable();
if (!nodeClass.hasColumn(columnName)) {
AttributeColumn newColumn = nodeClass.addColumn(columnName, type);
nodeColumns[i - 1] = new GDFColumn(newColumn);
report.log("Node attribute " + columnName + " (" + type.getTypeString() + ")");
} else {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat8", columnName), Issue.Level.SEVERE));
}
}
}
}
private void findEdgeColumns(String line) throws Exception {
String[] columns = line.split(",");
edgeColumns = new GDFColumn[columns.length - 2];
for (int i = 2; i < columns.length; i++) {
String columnString = columns[i];
String typeString = "";
String columnName = "";
AttributeType type = AttributeType.STRING;
try {
typeString = columnString.substring(columnString.lastIndexOf(" ")).trim().toLowerCase();
} catch (IndexOutOfBoundsException e) {
}
try {
int end = columnString.lastIndexOf(" ");
if (end != -1) {
columnName = columnString.substring(0, end).trim().toLowerCase();
} else {
columnName = columnString.trim().toLowerCase();
}
} catch (IndexOutOfBoundsException e) {
}
//Check error
if (columnName.isEmpty()) {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat2"), Issue.Level.SEVERE));
columnName = "default" + i;
}
if (typeString.isEmpty()) {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat6", columnName), Issue.Level.INFO));
typeString = "varchar";
}
//Clean parenthesis
typeString = typeString.replaceAll("\\([0-9]*\\)", "");
if (typeString.equals("varchar")) {
type = AttributeType.STRING;
} else if (typeString.equals("bool")) {
type = AttributeType.BOOLEAN;
} else if (typeString.equals("boolean")) {
type = AttributeType.BOOLEAN;
} else if (typeString.equals("integer")) {
type = AttributeType.INT;
} else if (typeString.equals("tinyint")) {
type = AttributeType.INT;
} else if (typeString.equals("int")) {
type = AttributeType.INT;
} else if (typeString.equals("double")) {
type = AttributeType.DOUBLE;
} else if (typeString.equals("float")) {
type = AttributeType.FLOAT;
} else {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat5", typeString), Issue.Level.WARNING));
}
if (columnName.equals("color")) {
edgeColumns[i - 2] = new GDFColumn(GDFColumn.EdgeGuessColumn.COLOR);
report.log("Edge property found: color");
} else if (columnName.equals("visible")) {
edgeColumns[i - 2] = new GDFColumn(GDFColumn.EdgeGuessColumn.VISIBLE);
report.log("Edge property found: visible");
} else if (columnName.equals("weight")) {
edgeColumns[i - 2] = new GDFColumn(GDFColumn.EdgeGuessColumn.WEIGHT);
report.log("Edge property found: weight");
} else if (columnName.equals("directed")) {
edgeColumns[i - 2] = new GDFColumn(GDFColumn.EdgeGuessColumn.DIRECTED);
report.log("Edge property found: directed");
} else if (columnName.equals("label")) {
edgeColumns[i - 2] = new GDFColumn(GDFColumn.EdgeGuessColumn.LABEL);
report.log("Edge property found: label");
} else if (columnName.equals("labelvisible")) {
edgeColumns[i - 2] = new GDFColumn(GDFColumn.EdgeGuessColumn.LABELVISIBLE);
report.log("Edge property found: labelvisible");
} else {
AttributeTable edgeClass = container.getAttributeModel().getEdgeTable();
if (!edgeClass.hasColumn(columnName)) {
AttributeColumn newColumn = edgeClass.addColumn(columnName, type);
edgeColumns[i - 2] = new GDFColumn(newColumn);
report.log("Edge attribute " + columnName + " (" + type.getTypeString() + ")");
} else {
report.logIssue(new Issue(NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat9", columnName), Issue.Level.SEVERE));
}
}
}
}
private boolean isNodeFirstLine(String line) {
for (String s : nodeLineStart) {
if (line.contains(s)) {
return true;
}
}
return false;
}
private boolean isEdgeFirstLine(String line) {
for (String s : edgeLineStart) {
if (line.contains(s)) {
return true;
}
}
return false;
}
private void setNodeData(NodeDraft node, GDFColumn column, String data) throws Exception {
if (column.getNodeColumn() != null) {
try {
switch (column.getNodeColumn()) {
case X:
node.setX(Float.parseFloat(data));
break;
case Y:
node.setY(Float.parseFloat(data));
break;
case COLOR:
String[] rgb = data.split(",");
if (rgb.length == 3) {
node.setColor(rgb[0], rgb[1], rgb[2]);
}
break;
case FIXED:
node.setFixed(Boolean.parseBoolean(data));
break;
case HEIGHT:
break;
case WIDTH:
node.setSize(Float.parseFloat(data));
break;
case LABEL:
node.setLabel(data);
break;
case LABELVISIBLE:
node.setLabelVisible(Boolean.parseBoolean(data));
break;
case VISIBLE:
node.setVisible(Boolean.parseBoolean(data));
break;
}
} catch (Exception e) {
String message = NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat3", column.getNodeColumn(), node, data);
report.logIssue(new Issue(message, Issue.Level.WARNING, e));
}
} else if (column.getAttributeColumn() != null) {
try {
node.addAttributeValue(column.getAttributeColumn(), data);
} catch (Exception e) {
String message = NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat4", column.getAttributeColumn().getType(), column.getAttributeColumn().getTitle(), node);
report.logIssue(new Issue(message, Issue.Level.WARNING, e));
}
}
}
private void setEdgeData(EdgeDraft edge, GDFColumn column, String data) throws Exception {
if (column.getEdgeColumn() != null) {
try {
switch (column.getEdgeColumn()) {
case COLOR:
String[] rgb = data.split(",");
if (rgb.length == 3) {
edge.setColor(rgb[0], rgb[1], rgb[2]);
}
break;
case VISIBLE:
edge.setVisible(Boolean.parseBoolean(data));
break;
case WEIGHT:
edge.setWeight(Float.parseFloat(data));
break;
case DIRECTED:
if (Boolean.parseBoolean(data)) {
edge.setType(EdgeDraft.EdgeType.DIRECTED);
} else {
edge.setType(EdgeDraft.EdgeType.UNDIRECTED);
}
break;
case LABEL:
edge.setLabel(data);
break;
case LABELVISIBLE:
edge.setLabelVisible(Boolean.parseBoolean(data));
break;
}
} catch (Exception e) {
String message = NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat3", column.getEdgeColumn(), data);
report.logIssue(new Issue(message, Issue.Level.WARNING, e));
}
} else if (column.getAttributeColumn() != null) {
try {
edge.addAttributeValue(column.getAttributeColumn(), data);
} catch (Exception e) {
String message = NbBundle.getMessage(ImporterGDF.class, "importerGDF_error_dataformat4", column.getAttributeColumn().getType(), column.getAttributeColumn().getTitle(), edge);
report.logIssue(new Issue(message, Issue.Level.WARNING, e));
}
}
}
public void setReader(Reader reader) {
this.reader = reader;
}
public ContainerLoader getContainer() {
return container;
}
public Report getReport() {
return report;
}
public boolean cancel() {
cancel = true;
return true;
}
public void setProgressTicket(ProgressTicket progressTicket) {
this.progressTicket = progressTicket;
}
private static class GDFColumn {
public enum NodeGuessColumn {
X, Y, VISIBLE, FIXED, STYLE, COLOR, WIDTH, HEIGHT, LABEL, LABELVISIBLE
};
public enum EdgeGuessColumn {
VISIBLE, COLOR, WEIGHT, DIRECTED, LABEL, LABELVISIBLE
};
private AttributeColumn column;
private NodeGuessColumn nodeColumn;
private EdgeGuessColumn edgeColumn;
public GDFColumn(NodeGuessColumn column) {
this.nodeColumn = column;
}
public GDFColumn(EdgeGuessColumn column) {
this.edgeColumn = column;
}
public GDFColumn(AttributeColumn column) {
this.column = column;
}
public NodeGuessColumn getNodeColumn() {
return nodeColumn;
}
public EdgeGuessColumn getEdgeColumn() {
return edgeColumn;
}
public AttributeColumn getAttributeColumn() {
return column;
}
}
}