2
Copyright 2008-2010 Gephi
3
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>,
4
Sebastien Heymann <sebastien.heymann@gephi.org>
5
Website : http://www.gephi.org
7
This file is part of Gephi.
9
Gephi is free software: you can redistribute it and/or modify
10
it under the terms of the GNU Affero General Public License as
11
published by the Free Software Foundation, either version 3 of the
12
License, or (at your option) any later version.
14
Gephi is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
GNU Affero General Public License for more details.
19
You should have received a copy of the GNU Affero General Public License
20
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
22
package org.gephi.io.exporter.plugin;
24
import java.awt.Color;
25
import java.io.Writer;
26
import javax.xml.parsers.DocumentBuilder;
27
import javax.xml.parsers.DocumentBuilderFactory;
28
import javax.xml.parsers.ParserConfigurationException;
29
import javax.xml.transform.OutputKeys;
30
import javax.xml.transform.Result;
31
import javax.xml.transform.Source;
32
import javax.xml.transform.Transformer;
33
import javax.xml.transform.TransformerConfigurationException;
34
import javax.xml.transform.TransformerException;
35
import javax.xml.transform.TransformerFactory;
36
import javax.xml.transform.dom.DOMSource;
37
import javax.xml.transform.stream.StreamResult;
38
import org.gephi.data.attributes.api.AttributeColumn;
39
import org.gephi.data.attributes.api.AttributeModel;
40
import org.gephi.data.attributes.api.AttributeOrigin;
41
import org.gephi.data.attributes.type.TimeInterval;
42
import org.gephi.dynamic.DynamicUtilities;
43
import org.gephi.dynamic.api.DynamicModel;
44
import org.gephi.graph.api.Edge;
45
import org.gephi.graph.api.EdgeIterable;
46
import org.gephi.graph.api.Graph;
47
import org.gephi.graph.api.GraphModel;
48
import org.gephi.graph.api.HierarchicalGraph;
49
import org.gephi.graph.api.Node;
50
import org.gephi.graph.api.NodeData;
51
import org.gephi.io.exporter.api.FileType;
52
import org.gephi.io.exporter.spi.GraphExporter;
53
import org.gephi.io.exporter.spi.CharacterExporter;
54
import org.gephi.project.api.Workspace;
55
import org.gephi.utils.longtask.spi.LongTask;
56
import org.gephi.utils.progress.Progress;
57
import org.gephi.utils.progress.ProgressTicket;
58
import org.openide.util.NbBundle;
59
import org.w3c.dom.Document;
60
import org.w3c.dom.Element;
61
import org.w3c.dom.Text;
65
* @author Sebastien Heymann
66
* @author Mathieu Bastian
68
public class ExporterGraphML implements GraphExporter, CharacterExporter, LongTask {
70
private boolean cancel = false;
71
private ProgressTicket progressTicket;
72
private Workspace workspace;
73
private Writer writer;
74
private boolean exportVisible;
75
private GraphModel graphModel;
76
private AttributeModel attributeModel;
78
private boolean normalize = false;
79
private boolean exportColors = true;
80
private boolean exportPosition = true;
81
private boolean exportSize = true;
82
private boolean exportAttributes = true;
83
private boolean exportHierarchy = false;
85
private float minSize;
86
private float maxSize;
94
private TimeInterval visibleInterval;
96
public boolean execute() {
97
attributeModel = workspace.getLookup().lookup(AttributeModel.class);
98
graphModel = workspace.getLookup().lookup(GraphModel.class);
99
HierarchicalGraph graph = null;
101
graph = graphModel.getHierarchicalGraphVisible();
103
graph = graphModel.getHierarchicalGraph();
105
DynamicModel dynamicModel = workspace.getLookup().lookup(DynamicModel.class);
106
visibleInterval = dynamicModel != null && exportVisible ? dynamicModel.getVisibleInterval() : new TimeInterval();
108
exportData(createDocument(), graph, attributeModel);
109
} catch (Exception e) {
110
graph.readUnlockAll();
111
throw new RuntimeException(e);
117
public Document createDocument() throws ParserConfigurationException {
118
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
119
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
120
final Document document = documentBuilder.newDocument();
121
document.setXmlVersion("1.0");
122
document.setXmlStandalone(true);
126
private void transform(Document document) throws TransformerConfigurationException, TransformerException {
127
Source source = new DOMSource(document);
128
Result result = new StreamResult(writer);
129
Transformer transformer = TransformerFactory.newInstance().newTransformer();
130
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
131
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
132
transformer.transform(source, result);
136
public Schema getSchema() {
138
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
139
return sf.newSchema(new URL("http://www.gexf.net/1.1draft/gexf.xsd"));
140
} catch (MalformedURLException ex) {
141
Exceptions.printStackTrace(ex);
142
} catch (SAXException ex) {
143
Exceptions.printStackTrace(ex);
148
public boolean exportData(Document document, HierarchicalGraph graph, AttributeModel model) throws Exception {
149
Progress.start(progressTicket);
154
calculateMinMax(graph);
156
//Calculate progress units count
158
if (graphModel.isHierarchical()) {
159
HierarchicalGraph hgraph = graphModel.getHierarchicalGraph();
160
max = hgraph.getNodeCount() + hgraph.getEdgeCount();
162
max = graph.getNodeCount() + graph.getEdgeCount();
164
Progress.switchToDeterminate(progressTicket, max);
166
Element root = document.createElementNS("http://graphml.graphdrawing.org/xmlns", "graphml");
167
document.appendChild(root);
169
createKeys(document, root);
171
Element graphE = createGraph(document, graph);
172
root.appendChild(graphE);
174
graph.readUnlockAll();
180
Progress.finish(progressTicket);
184
private void createKeys(Document document, Element root) {
185
Element nodeLabelKeyE = document.createElement("key");
186
nodeLabelKeyE.setAttribute("id", "label");
187
nodeLabelKeyE.setAttribute("attr.name", "label");
188
nodeLabelKeyE.setAttribute("attr.type", "string");
189
nodeLabelKeyE.setAttribute("for", "node");
190
root.appendChild(nodeLabelKeyE);
192
Element edgeLabelKeyE = document.createElement("key");
193
edgeLabelKeyE.setAttribute("id", "edgelabel");
194
edgeLabelKeyE.setAttribute("attr.name", "Edge Label");
195
edgeLabelKeyE.setAttribute("attr.type", "string");
196
edgeLabelKeyE.setAttribute("for", "edge");
197
root.appendChild(edgeLabelKeyE);
199
Element weightKeyE = document.createElement("key");
200
weightKeyE.setAttribute("id", "weight");
201
weightKeyE.setAttribute("attr.name", "weight");
202
weightKeyE.setAttribute("attr.type", "double");
203
weightKeyE.setAttribute("for", "edge");
204
root.appendChild(weightKeyE);
206
Element edgeIdKeyE = document.createElement("key");
207
edgeIdKeyE.setAttribute("id", "edgeid");
208
edgeIdKeyE.setAttribute("attr.name", "Edge Id");
209
edgeIdKeyE.setAttribute("attr.type", "string");
210
edgeIdKeyE.setAttribute("for", "edge");
211
root.appendChild(edgeIdKeyE);
214
Element colorRKeyE = document.createElement("key");
215
colorRKeyE.setAttribute("id", "r");
216
colorRKeyE.setAttribute("attr.name", "r");
217
colorRKeyE.setAttribute("attr.type", "int");
218
colorRKeyE.setAttribute("for", "node");
219
root.appendChild(colorRKeyE);
221
Element colorGKeyE = document.createElement("key");
222
colorGKeyE.setAttribute("id", "g");
223
colorGKeyE.setAttribute("attr.name", "g");
224
colorGKeyE.setAttribute("attr.type", "int");
225
colorGKeyE.setAttribute("for", "node");
226
root.appendChild(colorGKeyE);
228
Element colorBKeyE = document.createElement("key");
229
colorBKeyE.setAttribute("id", "b");
230
colorBKeyE.setAttribute("attr.name", "b");
231
colorBKeyE.setAttribute("attr.type", "int");
232
colorBKeyE.setAttribute("for", "node");
233
root.appendChild(colorBKeyE);
236
if (exportPosition) {
237
Element positionKeyE = document.createElement("key");
238
positionKeyE.setAttribute("id", "x");
239
positionKeyE.setAttribute("attr.name", "x");
240
positionKeyE.setAttribute("attr.type", "float");
241
positionKeyE.setAttribute("for", "node");
242
root.appendChild(positionKeyE);
244
Element positionKey2E = document.createElement("key");
245
positionKey2E.setAttribute("id", "y");
246
positionKey2E.setAttribute("attr.name", "y");
247
positionKey2E.setAttribute("attr.type", "float");
248
positionKey2E.setAttribute("for", "node");
249
root.appendChild(positionKey2E);
251
if (minZ != 0f || maxZ != 0f) {
252
Element positionKey3E = document.createElement("key");
253
positionKey3E.setAttribute("id", "z");
254
positionKey3E.setAttribute("attr.name", "z");
255
positionKey3E.setAttribute("attr.type", "float");
256
positionKey3E.setAttribute("for", "node");
257
root.appendChild(positionKey3E);
262
Element sizeKeyE = document.createElement("key");
263
sizeKeyE.setAttribute("id", "size");
264
sizeKeyE.setAttribute("attr.name", "size");
265
sizeKeyE.setAttribute("attr.type", "float");
266
sizeKeyE.setAttribute("for", "node");
267
root.appendChild(sizeKeyE);
271
if (attributeModel != null && exportAttributes) {
273
for (AttributeColumn column : attributeModel.getNodeTable().getColumns()) {
274
if (!column.getOrigin().equals(AttributeOrigin.PROPERTY)) {
275
Element attributeE = createAttribute(document, column);
276
attributeE.setAttribute("for", "node");
277
root.appendChild(attributeE);
281
for (AttributeColumn column : attributeModel.getEdgeTable().getColumns()) {
282
if (!column.getOrigin().equals(AttributeOrigin.PROPERTY)) {
284
Element attributeE = createAttribute(document, column);
285
attributeE.setAttribute("for", "edge");
286
root.appendChild(attributeE);
292
private Element createGraph(Document document, Graph graph) throws Exception {
293
Element graphE = document.createElement("graph");
295
if (graphModel.isDirected()) {
296
graphE.setAttribute("edgedefault", "directed");
298
graphE.setAttribute("edgedefault", "undirected"); // defaultValue
302
createNodes(document, graphE, graph, null);
305
createEdges(document, graphE, graph);
310
private Element createAttribute(Document document, AttributeColumn column) {
311
Element attributeE = document.createElement("key");
312
attributeE.setAttribute("id", column.getId());
313
attributeE.setAttribute("attr.name", column.getTitle());
314
switch (column.getType()) {
316
attributeE.setAttribute("attr.type", "int");
319
attributeE.setAttribute("attr.type", column.getType().getTypeString().toLowerCase());
322
if (column.getDefaultValue() != null) {
323
Element defaultE = document.createElement("default");
324
Text defaultTextE = document.createTextNode(column.getDefaultValue().toString());
325
defaultE.appendChild(defaultTextE);
330
private Element createNodeAttvalue(Document document, AttributeColumn column, Node n) throws Exception {
331
int index = column.getIndex();
332
if (n.getNodeData().getAttributes().getValue(index) != null) {
333
Object val = n.getNodeData().getAttributes().getValue(index);
334
val = DynamicUtilities.getDynamicValue(val, visibleInterval.getLow(), visibleInterval.getHigh());
335
String value = val.toString();
336
String id = column.getId();
338
Element attvalueE = document.createElement("data");
339
attvalueE.setAttribute("key", id);
340
attvalueE.setTextContent(value);
346
private Element createEdgeAttvalue(Document document, AttributeColumn column, Edge e) throws Exception {
347
int index = column.getIndex();
348
if (e.getEdgeData().getAttributes().getValue(index) != null) {
349
Object val = e.getEdgeData().getAttributes().getValue(index);
350
val = DynamicUtilities.getDynamicValue(val, visibleInterval.getLow(), visibleInterval.getHigh());
351
String value = val.toString();
352
String id = column.getId();
354
Element attvalueE = document.createElement("data");
355
attvalueE.setAttribute("key", id);
356
attvalueE.setTextContent(value);
362
private void createNodes(Document document, Element parentE, Graph graph, Node nodeParent) throws Exception {
364
if (nodeParent != null) {
365
Element graphE = document.createElement("graph");
367
if (graphModel.isDirected()) {
368
graphE.setAttribute("edgedefault", "directed");
370
graphE.setAttribute("edgedefault", "undirected"); // defaultValue
372
// we are inside the tree
373
HierarchicalGraph hgraph = graphModel.getHierarchicalGraph();
374
for (Node n : hgraph.getChildren(nodeParent)) {
375
Element childE = createNode(document, graph, n);
376
graphE.appendChild(childE);
378
parentE.appendChild(graphE);
379
} else if (exportHierarchy && graphModel.isHierarchical()) {
380
// we are on the top of the tree
381
HierarchicalGraph hgraph = graphModel.getHierarchicalGraph();
382
for (Node n : hgraph.getTopNodes()) {
383
Element nodeE = createNode(document, hgraph, n);
384
parentE.appendChild(nodeE);
388
for (Node n : graph.getNodes()) {
392
Element nodeE = createNode(document, graph, n);
393
parentE.appendChild(nodeE);
398
private Element createNode(Document document, Graph graph, Node n) throws Exception {
399
Element nodeE = document.createElement("node");
400
nodeE.setAttribute("id", n.getNodeData().getId());
403
if (n.getNodeData().getLabel() != null && !n.getNodeData().getLabel().isEmpty()) {
404
Element labelE = createNodeLabel(document, n);
405
nodeE.appendChild(labelE);
409
if (attributeModel != null && exportAttributes) {
410
for (AttributeColumn column : attributeModel.getNodeTable().getColumns()) {
411
if (!column.getOrigin().equals(AttributeOrigin.PROPERTY)) {
413
Element attvalueE = createNodeAttvalue(document, column, n);
414
if (attvalueE != null) {
415
nodeE.appendChild(attvalueE);
423
Element sizeE = createNodeSize(document, n);
424
nodeE.appendChild(sizeE);
427
Element colorE = createNodeColorR(document, n);
428
nodeE.appendChild(colorE);
430
colorE = createNodeColorG(document, n);
431
nodeE.appendChild(colorE);
433
colorE = createNodeColorB(document, n);
434
nodeE.appendChild(colorE);
436
if (exportPosition) {
437
Element positionXE = createNodePositionX(document, n);
438
nodeE.appendChild(positionXE);
439
Element positionYE = createNodePositionY(document, n);
440
nodeE.appendChild(positionYE);
441
if (minZ != 0f || maxZ != 0f) {
442
Element positionZE = createNodePositionZ(document, n);
443
nodeE.appendChild(positionZE);
448
if (exportHierarchy && graphModel.isHierarchical()) {
449
HierarchicalGraph hgraph = graphModel.getHierarchicalGraph();
450
int childCount = hgraph.getChildrenCount(n);
451
if (childCount != 0) {
452
createNodes(document, nodeE, graph, n);
455
Progress.progress(progressTicket);
460
private void createEdges(Document document, Element edgesE, Graph graph) throws Exception {
463
HierarchicalGraph hgraph = graphModel.getHierarchicalGraph();
464
if (exportHierarchy && graphModel.isHierarchical()) {
465
it = hgraph.getEdgesTree();
467
it = hgraph.getEdgesAndMetaEdges();
469
for (Edge e : it.toArray()) {
473
Element edgeE = createEdge(document, e);
474
edgesE.appendChild(edgeE);
478
private Element createEdge(Document document, Edge e) throws Exception {
479
Element edgeE = document.createElement("edge");
481
edgeE.setAttribute("source", e.getSource().getNodeData().getId());
482
edgeE.setAttribute("target", e.getTarget().getNodeData().getId());
484
if (e.getEdgeData().getId() != null && !e.getEdgeData().getId().isEmpty() && !String.valueOf(e.getId()).equals(e.getEdgeData().getId())) {
485
Element idE = createEdgeId(document, e);
486
edgeE.appendChild(idE);
490
if (e.getEdgeData().getLabel() != null && !e.getEdgeData().getLabel().isEmpty()) {
491
Element labelE = createEdgeLabel(document, e);
492
edgeE.appendChild(labelE);
495
Element weightE = createEdgeWeight(document, e);
496
edgeE.appendChild(weightE);
498
if (e.isDirected() && !graphModel.isDirected()) {
499
edgeE.setAttribute("type", "directed");
500
} else if (!e.isDirected() && graphModel.isDirected()) {
501
edgeE.setAttribute("type", "undirected");
505
if (attributeModel != null) {
506
for (AttributeColumn column : attributeModel.getEdgeTable().getColumns()) {
507
if (!column.getOrigin().equals(AttributeOrigin.PROPERTY)) {
509
Element attvalueE = createEdgeAttvalue(document, column, e);
510
if (attvalueE != null) {
511
edgeE.appendChild(attvalueE);
516
Progress.progress(progressTicket);
521
private Element createNodeSize(Document document, Node n) throws Exception {
522
Element sizeE = document.createElement("data");
523
float size = n.getNodeData().getSize();
525
size = (size - minSize) / (maxSize - minSize);
527
sizeE.setAttribute("key", "size");
528
sizeE.setTextContent("" + size);
533
private Element createNodeColorR(Document document, Node n) throws Exception {
534
int r = Math.round(n.getNodeData().r() * 255f);
535
Element colorE = document.createElement("data");
536
colorE.setAttribute("key", "r");
537
colorE.setTextContent("" + r);
541
private Element createNodeColorG(Document document, Node n) throws Exception {
542
int g = Math.round(n.getNodeData().g() * 255f);
543
Element colorE = document.createElement("data");
544
colorE.setAttribute("key", "g");
545
colorE.setTextContent("" + g);
549
private Element createNodeColorB(Document document, Node n) throws Exception {
550
int b = Math.round(n.getNodeData().b() * 255f);
551
Element colorE = document.createElement("data");
552
colorE.setAttribute("key", "b");
553
colorE.setTextContent("" + b);
557
private Element createNodePositionX(Document document, Node n) throws Exception {
558
Element positionXE = document.createElement("data");
559
float x = n.getNodeData().x();
560
if (normalize && x != 0.0) {
561
x = (x - minX) / (maxX - minX);
563
positionXE.setAttribute("key", "x");
564
positionXE.setTextContent("" + x);
568
private Element createNodePositionY(Document document, Node n) throws Exception {
569
Element positionYE = document.createElement("data");
570
float y = n.getNodeData().y();
571
if (normalize && y != 0.0) {
572
y = (y - minY) / (maxY - minY);
574
positionYE.setAttribute("key", "y");
575
positionYE.setTextContent("" + y);
580
private Element createNodePositionZ(Document document, Node n) throws Exception {
581
Element positionZE = document.createElement("data");
582
float z = n.getNodeData().z();
583
if (normalize && z != 0.0) {
584
z = (z - minZ) / (maxZ - minZ);
586
positionZE.setAttribute("key", "z");
587
positionZE.setTextContent("" + z);
592
private Element createNodeLabel(Document document, Node n) throws Exception {
593
Element labelE = document.createElement("data");
594
labelE.setAttribute("key", "label");
595
labelE.setTextContent(n.getNodeData().getLabel());
600
private Element createEdgeId(Document document, Edge e) throws Exception {
601
Element idE = document.createElement("data");
602
idE.setAttribute("key", "edgeid");
603
idE.setTextContent(e.getEdgeData().getId());
608
private Element createEdgeWeight(Document document, Edge e) throws Exception {
609
Element weightE = document.createElement("data");
610
weightE.setAttribute("key", "weight");
611
weightE.setTextContent(Float.toString(e.getWeight(visibleInterval.getLow(), visibleInterval.getHigh())));
616
private Element createEdgeLabel(Document document, Edge e) throws Exception {
617
Element labelE = document.createElement("data");
618
labelE.setAttribute("key", "edgelabel");
619
labelE.setTextContent(e.getEdgeData().getLabel());
624
private void calculateMinMax(Graph graph) {
625
minX = Float.POSITIVE_INFINITY;
626
maxX = Float.NEGATIVE_INFINITY;
627
minY = Float.POSITIVE_INFINITY;
628
maxY = Float.NEGATIVE_INFINITY;
629
minZ = Float.POSITIVE_INFINITY;
630
maxZ = Float.NEGATIVE_INFINITY;
631
minSize = Float.POSITIVE_INFINITY;
632
maxSize = Float.NEGATIVE_INFINITY;
634
for (Node node : graph.getNodes()) {
635
NodeData nodeData = node.getNodeData();
636
minX = Math.min(minX, nodeData.x());
637
maxX = Math.max(maxX, nodeData.x());
638
minY = Math.min(minY, nodeData.y());
639
maxY = Math.max(maxY, nodeData.y());
640
minZ = Math.min(minZ, nodeData.z());
641
maxZ = Math.max(maxZ, nodeData.z());
642
minSize = Math.min(minSize, nodeData.getSize());
643
maxSize = Math.max(maxSize, nodeData.getSize());
647
public boolean cancel() {
652
public void setProgressTicket(ProgressTicket progressTicket) {
653
this.progressTicket = progressTicket;
656
public String getName() {
657
return NbBundle.getMessage(getClass(), "ExporterGraphML_name");
660
public FileType[] getFileTypes() {
661
FileType ft = new FileType(".graphml", NbBundle.getMessage(getClass(), "fileType_GraphML_Name"));
662
return new FileType[]{ft};
665
public void setExportAttributes(boolean exportAttributes) {
666
this.exportAttributes = exportAttributes;
669
public void setExportColors(boolean exportColors) {
670
this.exportColors = exportColors;
673
public void setExportPosition(boolean exportPosition) {
674
this.exportPosition = exportPosition;
677
public void setExportSize(boolean exportSize) {
678
this.exportSize = exportSize;
681
public void setNormalize(boolean normalize) {
682
this.normalize = normalize;
685
public boolean isExportAttributes() {
686
return exportAttributes;
689
public boolean isExportColors() {
693
public boolean isExportPosition() {
694
return exportPosition;
697
public boolean isExportSize() {
701
public boolean isNormalize() {
705
public boolean isExportVisible() {
706
return exportVisible;
709
public void setExportVisible(boolean exportVisible) {
710
this.exportVisible = exportVisible;
713
public void setWriter(Writer writer) {
714
this.writer = writer;
717
public Workspace getWorkspace() {
721
public void setWorkspace(Workspace workspace) {
722
this.workspace = workspace;
725
public boolean isExportHierarchy() {
726
return exportHierarchy;
729
public void setExportHierarchy(boolean exportHierarchy) {
730
this.exportHierarchy = exportHierarchy;