/* 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.importer.plugin.file; import java.io.Reader; import java.util.HashMap; import javax.xml.stream.Location; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLReporter; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.events.XMLEvent; import org.gephi.data.attributes.api.AttributeColumn; import org.gephi.data.attributes.api.AttributeOrigin; import org.gephi.data.attributes.api.AttributeType; import org.gephi.io.importer.api.ContainerLoader; import org.gephi.io.importer.api.EdgeDefault; import org.gephi.io.importer.api.EdgeDraft; import org.gephi.io.importer.api.Issue; import org.gephi.io.importer.api.NodeDraft; import org.gephi.io.importer.api.PropertiesAssociations; import org.gephi.io.importer.api.PropertiesAssociations.EdgeProperties; import org.gephi.io.importer.api.PropertiesAssociations.NodeProperties; 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 */ public class ImporterGraphML implements FileImporter, LongTask { //GEXF private static final String GRAPHML = "graphml"; private static final String GRAPH = "graph"; private static final String GRAPH_DEFAULT_EDGETYPE = "edgedefault"; private static final String GRAPH_ID = "id"; private static final String NODE = "node"; private static final String NODE_ID = "id"; private static final String EDGE = "edge"; private static final String EDGE_ID = "id"; private static final String EDGE_SOURCE = "source"; private static final String EDGE_TARGET = "target"; private static final String EDGE_DIRECTED = "directed"; private static final String ATTRIBUTE = "key"; private static final String ATTRIBUTE_ID = "id"; private static final String ATTRIBUTE_TITLE = "attr.name"; private static final String ATTRIBUTE_TYPE = "attr.type"; private static final String ATTRIBUTE_DEFAULT = "default"; private static final String ATTRIBUTE_FOR = "for"; private static final String ATTVALUE = "data"; private static final String ATTVALUE_FOR = "key"; //Architecture private Reader reader; private ContainerLoader container; private boolean cancel; private Report report; private ProgressTicket progress; private XMLStreamReader xmlReader; private PropertiesAssociations properties = new PropertiesAssociations(); private HashMap<String, NodeProperties> nodePropertiesAttributes = new HashMap<String, NodeProperties>(); private HashMap<String, EdgeProperties> edgePropertiesAttributes = new HashMap<String, EdgeProperties>(); public ImporterGraphML() { //Default node associations properties.addNodePropertyAssociation(NodeProperties.LABEL, "label"); properties.addNodePropertyAssociation(NodeProperties.LABEL, "d3"); // Default node label used by yEd from yworks.com. properties.addNodePropertyAssociation(NodeProperties.X, "x"); properties.addNodePropertyAssociation(NodeProperties.Y, "y"); properties.addNodePropertyAssociation(NodeProperties.X, "xpos"); properties.addNodePropertyAssociation(NodeProperties.Y, "ypos"); properties.addNodePropertyAssociation(NodeProperties.Z, "z"); properties.addNodePropertyAssociation(NodeProperties.SIZE, "size"); properties.addNodePropertyAssociation(NodeProperties.R, "r"); properties.addNodePropertyAssociation(NodeProperties.G, "g"); properties.addNodePropertyAssociation(NodeProperties.B, "b"); //Default edge associations properties.addEdgePropertyAssociation(EdgeProperties.LABEL, "label"); properties.addEdgePropertyAssociation(EdgeProperties.LABEL, "edgelabel"); properties.addEdgePropertyAssociation(EdgeProperties.LABEL, "d7"); // Default edge label used by yEd from yworks.com. properties.addEdgePropertyAssociation(EdgeProperties.WEIGHT, "weight"); properties.addEdgePropertyAssociation(EdgeProperties.WEIGHT, "Edge Weight"); properties.addEdgePropertyAssociation(EdgeProperties.ID, "id"); properties.addEdgePropertyAssociation(EdgeProperties.ID, "edgeid"); properties.addEdgePropertyAssociation(EdgeProperties.R, "r"); properties.addEdgePropertyAssociation(EdgeProperties.G, "g"); properties.addEdgePropertyAssociation(EdgeProperties.B, "b"); } public boolean execute(ContainerLoader container) { this.container = container; this.report = new Report(); Progress.start(progress); try { XMLInputFactory inputFactory = XMLInputFactory.newInstance(); if (inputFactory.isPropertySupported("javax.xml.stream.isValidating")) { inputFactory.setProperty("javax.xml.stream.isValidating", Boolean.FALSE); } inputFactory.setXMLReporter(new XMLReporter() { @Override public void report(String message, String errorType, Object relatedInformation, Location location) throws XMLStreamException { System.out.println("Error:" + errorType + ", message : " + message); } }); xmlReader = inputFactory.createXMLStreamReader(reader); while (xmlReader.hasNext()) { Integer eventType = xmlReader.next(); if (eventType.equals(XMLEvent.START_ELEMENT)) { String name = xmlReader.getLocalName(); if (GRAPHML.equalsIgnoreCase(name)) { } else if (GRAPH.equalsIgnoreCase(name)) { readGraph(xmlReader); } else if (NODE.equalsIgnoreCase(name)) { readNode(xmlReader, null); } else if (EDGE.equalsIgnoreCase(name)) { readEdge(xmlReader); } else if (ATTRIBUTE.equalsIgnoreCase(name)) { readAttribute(xmlReader); } } else if (eventType.equals(XMLStreamReader.END_ELEMENT)) { String name = xmlReader.getLocalName(); if (NODE.equalsIgnoreCase(name)) { } } } xmlReader.close(); } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException(e); } Progress.finish(progress); return !cancel; } private void readGraph(XMLStreamReader reader) throws Exception { String id = ""; String defaultEdgeType = ""; //Attributes for (int i = 0; i < reader.getAttributeCount(); i++) { String attName = reader.getAttributeName(i).getLocalPart(); if (GRAPH_DEFAULT_EDGETYPE.equalsIgnoreCase(attName)) { defaultEdgeType = reader.getAttributeValue(i); } else if (GRAPH_ID.equalsIgnoreCase(attName)) { id = reader.getAttributeValue(i); } } //Edge Type if (!defaultEdgeType.isEmpty()) { if (defaultEdgeType.equalsIgnoreCase("undirected")) { container.setEdgeDefault(EdgeDefault.UNDIRECTED); } else if (defaultEdgeType.equalsIgnoreCase("directed")) { container.setEdgeDefault(EdgeDefault.DIRECTED); } else { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_defaultedgetype", defaultEdgeType), Issue.Level.SEVERE)); } } } private void readNode(XMLStreamReader reader, NodeDraft parent) throws Exception { String id = ""; //Attributes for (int i = 0; i < reader.getAttributeCount(); i++) { String attName = reader.getAttributeName(i).getLocalPart(); if (NODE_ID.equalsIgnoreCase(attName)) { id = reader.getAttributeValue(i); } } if (id.isEmpty()) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_nodeid"), Issue.Level.SEVERE)); return; } NodeDraft node = null; if (container.nodeExists(id)) { node = container.getNode(id); } else { node = container.factory().newNodeDraft(); } node.setId(id); //Parent if (parent != null) { node.setParent(parent); } if (!container.nodeExists(id)) { container.addNode(node); } boolean end = false; while (reader.hasNext() && !end) { int type = reader.next(); switch (type) { case XMLStreamReader.START_ELEMENT: String name = xmlReader.getLocalName(); if (ATTVALUE.equalsIgnoreCase(xmlReader.getLocalName())) { readNodeAttValue(reader, node); } else if (NODE.equalsIgnoreCase(name)) { readNode(reader, node); } break; case XMLStreamReader.END_ELEMENT: if (NODE.equalsIgnoreCase(xmlReader.getLocalName())) { end = true; } break; } } } private void readNodeAttValue(XMLStreamReader reader, NodeDraft node) throws Exception { String fore = ""; String value = ""; for (int i = 0; i < reader.getAttributeCount(); i++) { String attName = reader.getAttributeName(i).getLocalPart(); if (ATTVALUE_FOR.equalsIgnoreCase(attName)) { fore = reader.getAttributeValue(i); } } if (fore.isEmpty()) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_datakey", node), Issue.Level.SEVERE)); return; } boolean end = false; while (reader.hasNext() && !end) { int xmltype = reader.next(); switch (xmltype) { case XMLStreamReader.CHARACTERS: if (!xmlReader.isWhiteSpace()) { value += xmlReader.getText(); } break; case XMLStreamReader.END_ELEMENT: if (ATTVALUE.equalsIgnoreCase(xmlReader.getLocalName())) { end = true; } break; } } if (!value.isEmpty()) { //Property NodeProperties prop = nodePropertiesAttributes.get(fore); if (prop != null) { try { switch (prop) { case X: node.setX(parseFloat(value)); break; case Y: node.setY(parseFloat(value)); break; case Z: node.setZ(parseFloat(value)); break; case SIZE: node.setSize(parseFloat(value)); break; case LABEL: node.setLabel(value); break; case R: if (node.getColor() == null) { node.setColor(Integer.parseInt(value), 0, 0); } else { node.setColor(Integer.parseInt(value), node.getColor().getGreen(), node.getColor().getBlue()); } break; case G: if (node.getColor() == null) { node.setColor(0, Integer.parseInt(value), 0); } else { node.setColor(node.getColor().getRed(), Integer.parseInt(value), node.getColor().getBlue()); } break; case B: if (node.getColor() == null) { node.setColor(0, 0, Integer.parseInt(value)); } else { node.setColor(node.getColor().getRed(), node.getColor().getGreen(), Integer.parseInt(value)); } break; } } catch (Exception e) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_datavalue", fore, node, prop.toString()), Issue.Level.SEVERE)); } return; } //Data attribute value AttributeColumn column = container.getAttributeModel().getNodeTable().getColumn(fore); if (column != null) { try { Object val = column.getType().parse(value); node.addAttributeValue(column, val); } catch (Exception e) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_datavalue", fore, node, column.getTitle()), Issue.Level.SEVERE)); } } } } private void readEdge(XMLStreamReader reader) throws Exception { String id = ""; String source = ""; String target = ""; String directed = ""; //Attributes for (int i = 0; i < reader.getAttributeCount(); i++) { String attName = reader.getAttributeName(i).getLocalPart(); if (EDGE_SOURCE.equalsIgnoreCase(attName)) { source = reader.getAttributeValue(i); } else if (EDGE_TARGET.equalsIgnoreCase(attName)) { target = reader.getAttributeValue(i); } else if (EDGE_ID.equalsIgnoreCase(attName)) { id = reader.getAttributeValue(i); } else if (EDGE_DIRECTED.equalsIgnoreCase(attName)) { directed = reader.getAttributeValue(i); } } EdgeDraft edge = container.factory().newEdgeDraft(); NodeDraft nodeSource = container.getNode(source); NodeDraft nodeTarget = container.getNode(target); edge.setSource(nodeSource); edge.setTarget(nodeTarget); //Type if (!directed.isEmpty()) { if (directed.equalsIgnoreCase("true")) { edge.setType(EdgeDraft.EdgeType.DIRECTED); } else if (directed.equalsIgnoreCase("false")) { edge.setType(EdgeDraft.EdgeType.UNDIRECTED); } else { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_edgetype", directed, edge), Issue.Level.SEVERE)); } } //Id if (!id.isEmpty()) { edge.setId(id); } boolean end = false; while (reader.hasNext() && !end) { int type = reader.next(); switch (type) { case XMLStreamReader.START_ELEMENT: if (ATTVALUE.equalsIgnoreCase(xmlReader.getLocalName())) { readEdgeAttValue(reader, edge); } break; case XMLStreamReader.END_ELEMENT: if (EDGE.equalsIgnoreCase(xmlReader.getLocalName())) { end = true; } break; } } container.addEdge(edge); } private void readEdgeAttValue(XMLStreamReader reader, EdgeDraft edge) throws Exception { String fore = ""; String value = ""; for (int i = 0; i < reader.getAttributeCount(); i++) { String attName = reader.getAttributeName(i).getLocalPart(); if (ATTVALUE_FOR.equalsIgnoreCase(attName)) { fore = reader.getAttributeValue(i); } } if (fore.isEmpty()) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_datakey", edge), Issue.Level.SEVERE)); return; } boolean end = false; while (reader.hasNext() && !end) { int xmltype = reader.next(); switch (xmltype) { case XMLStreamReader.CHARACTERS: if (!xmlReader.isWhiteSpace()) { value += xmlReader.getText(); } break; case XMLStreamReader.END_ELEMENT: if (ATTVALUE.equalsIgnoreCase(xmlReader.getLocalName())) { end = true; } break; } } if (!value.isEmpty()) { EdgeProperties prop = edgePropertiesAttributes.get(fore); if (prop != null) { try { switch (prop) { case WEIGHT: edge.setWeight(parseFloat(value)); break; case LABEL: edge.setLabel(value); break; case ID: edge.setId(value); break; case R: if (edge.getColor() == null) { edge.setColor(Integer.parseInt(value), 0, 0); } else { edge.setColor(Integer.parseInt(value), edge.getColor().getGreen(), edge.getColor().getBlue()); } break; case G: if (edge.getColor() == null) { edge.setColor(0, Integer.parseInt(value), 0); } else { edge.setColor(edge.getColor().getRed(), Integer.parseInt(value), edge.getColor().getBlue()); } break; case B: if (edge.getColor() == null) { edge.setColor(0, 0, Integer.parseInt(value)); } else { edge.setColor(edge.getColor().getRed(), edge.getColor().getGreen(), Integer.parseInt(value)); } break; } } catch (Exception e) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_datavalue", fore, edge, prop.toString()), Issue.Level.SEVERE)); } return; } //Data attribute value AttributeColumn column = container.getAttributeModel().getEdgeTable().getColumn(fore); if (column != null) { try { Object val = column.getType().parse(value); edge.addAttributeValue(column, val); } catch (Exception e) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_datavalue", fore, edge, column.getTitle()), Issue.Level.SEVERE)); } } } } private void readAttribute(XMLStreamReader reader) throws Exception { String id = ""; String type = ""; String title = ""; String defaultStr = ""; String forStr = ""; for (int i = 0; i < reader.getAttributeCount(); i++) { String attName = reader.getAttributeName(i).getLocalPart(); if (ATTRIBUTE_ID.equalsIgnoreCase(attName)) { id = reader.getAttributeValue(i); } else if (ATTRIBUTE_TYPE.equalsIgnoreCase(attName)) { type = reader.getAttributeValue(i); } else if (ATTRIBUTE_TITLE.equalsIgnoreCase(attName)) { title = reader.getAttributeValue(i); } else if (ATTRIBUTE_FOR.equalsIgnoreCase(attName)) { forStr = reader.getAttributeValue(i); } } if (title.isEmpty()) { title = id; } boolean property = false; if (!id.isEmpty()) { //Properties if (forStr.equalsIgnoreCase("node")) { NodeProperties prop = properties.getNodeProperty(id) == null ? properties.getNodeProperty(title) : properties.getNodeProperty(id); if (prop != null) { nodePropertiesAttributes.put(id, prop); report.log(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_log_nodeproperty", title)); property = true; } } else if (forStr.equalsIgnoreCase("edge")) { EdgeProperties prop = properties.getEdgeProperty(id) == null ? properties.getEdgeProperty(title) : properties.getEdgeProperty(id); if (prop != null) { edgePropertiesAttributes.put(id, prop); report.log(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_log_edgeproperty", title)); property = true; } } if (property) { return; } } else { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_attributeempty", title), Issue.Level.SEVERE)); return; } if (!property && type.isEmpty()) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_attributetype1", title), Issue.Level.SEVERE)); type = "string"; } if (!property) { //Class type if (forStr.isEmpty() || !(forStr.equalsIgnoreCase("node") || forStr.equalsIgnoreCase("edge") || forStr.equalsIgnoreCase("all"))) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_attributeclass", title), Issue.Level.SEVERE)); return; } //Default? boolean end = false; boolean defaultFlag = false; while (reader.hasNext() && !end) { int xmltype = reader.next(); switch (xmltype) { case XMLStreamReader.START_ELEMENT: if (ATTRIBUTE_DEFAULT.equalsIgnoreCase(xmlReader.getLocalName())) { defaultFlag = true; } break; case XMLStreamReader.CHARACTERS: if (defaultFlag && !xmlReader.isWhiteSpace()) { defaultStr = xmlReader.getText(); } break; case XMLStreamReader.END_ELEMENT: if (ATTRIBUTE.equalsIgnoreCase(xmlReader.getLocalName())) { end = true; } break; } } //Type AttributeType attributeType = AttributeType.STRING; if (type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("bool")) { attributeType = AttributeType.BOOLEAN; } else if (type.equalsIgnoreCase("integer") || type.equalsIgnoreCase("int")) { attributeType = AttributeType.INT; } else if (type.equalsIgnoreCase("long")) { attributeType = AttributeType.LONG; } else if (type.equalsIgnoreCase("float")) { attributeType = AttributeType.FLOAT; } else if (type.equalsIgnoreCase("double")) { attributeType = AttributeType.DOUBLE; } else if (type.equalsIgnoreCase("string")) { attributeType = AttributeType.STRING; } else if (type.equalsIgnoreCase("bigdecimal")) { attributeType = AttributeType.BIGDECIMAL; } else if (type.equalsIgnoreCase("biginteger")) { attributeType = AttributeType.BIGINTEGER; } else if (type.equalsIgnoreCase("byte")) { attributeType = AttributeType.BYTE; } else if (type.equalsIgnoreCase("char")) { attributeType = AttributeType.CHAR; } else if (type.equalsIgnoreCase("short")) { attributeType = AttributeType.SHORT; } else if (type.equalsIgnoreCase("listboolean")) { attributeType = AttributeType.LIST_BOOLEAN; } else if (type.equalsIgnoreCase("listint")) { attributeType = AttributeType.LIST_INTEGER; } else if (type.equalsIgnoreCase("listlong")) { attributeType = AttributeType.LIST_LONG; } else if (type.equalsIgnoreCase("listfloat")) { attributeType = AttributeType.LIST_FLOAT; } else if (type.equalsIgnoreCase("listdouble")) { attributeType = AttributeType.LIST_DOUBLE; } else if (type.equalsIgnoreCase("liststring")) { attributeType = AttributeType.LIST_STRING; } else if (type.equalsIgnoreCase("listbigdecimal")) { attributeType = AttributeType.LIST_BIGDECIMAL; } else if (type.equalsIgnoreCase("listbiginteger")) { attributeType = AttributeType.LIST_BIGINTEGER; } else if (type.equalsIgnoreCase("listbyte")) { attributeType = AttributeType.LIST_BYTE; } else if (type.equalsIgnoreCase("listchar")) { attributeType = AttributeType.LIST_CHARACTER; } else if (type.equalsIgnoreCase("listshort")) { attributeType = AttributeType.LIST_SHORT; } else { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_attributetype2", type), Issue.Level.SEVERE)); return; } //Default Object Object defaultValue = null; if (!defaultStr.isEmpty()) { try { defaultValue = attributeType.parse(defaultStr); report.log(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_log_default", defaultStr, title)); } catch (Exception e) { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_attributedefault", title, attributeType.getTypeString()), Issue.Level.SEVERE)); } } //Add to model if ("node".equalsIgnoreCase(forStr) || "all".equalsIgnoreCase(forStr)) { if (container.getAttributeModel().getNodeTable().hasColumn(id) || container.getAttributeModel().getNodeTable().hasColumn(title)) { report.log(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_attributecolumn_exist", id)); return; } container.getAttributeModel().getNodeTable().addColumn(id, title, attributeType, AttributeOrigin.DATA, defaultValue); report.log(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_log_nodeattribute", title, attributeType.getTypeString())); } else if ("edge".equalsIgnoreCase(forStr) || "all".equalsIgnoreCase(forStr)) { if (container.getAttributeModel().getEdgeTable().hasColumn(id) || container.getAttributeModel().getEdgeTable().hasColumn(title)) { report.log(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_attributecolumn_exist", id)); return; } container.getAttributeModel().getEdgeTable().addColumn(id, title, attributeType, AttributeOrigin.DATA, defaultValue); report.log(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_log_edgeattribute", title, attributeType.getTypeString())); } } else { report.logIssue(new Issue(NbBundle.getMessage(ImporterGraphML.class, "importerGraphML_error_attributeempty", title), Issue.Level.SEVERE)); } } private float parseFloat(String str) { str = str.replace(',', '.'); return Float.parseFloat(str); } 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.progress = progressTicket; } }