001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.stream.file; 033 034import java.awt.Color; 035import java.io.IOException; 036import java.net.URI; 037import java.net.URISyntaxException; 038import java.util.EnumMap; 039import java.util.HashMap; 040import java.util.HashSet; 041import java.util.regex.Pattern; 042 043import javax.xml.stream.XMLStreamException; 044import javax.xml.stream.events.XMLEvent; 045 046/** 047 * File source for the <a href="http://gexf.net/format/">GEXF</a> file format 048 * used by <a href="http://www.gephi.org">Gephi</a>. 049 * 050 * @author Guilhelm Savin 051 * 052 */ 053public class FileSourceGEXF extends FileSourceXML { 054 private static final Pattern IS_DOUBLE = Pattern 055 .compile("^-?\\d+([.]\\d+)?$"); 056 057 /** 058 * The GEXF parser. 059 */ 060 protected GEXFParser parser; 061 062 /* 063 * (non-Javadoc) 064 * 065 * @see org.graphstream.stream.file.FileSourceXML#afterStartDocument() 066 */ 067 protected void afterStartDocument() throws IOException, XMLStreamException { 068 parser = new GEXFParser(); 069 parser.__gexf(); 070 } 071 072 /* 073 * (non-Javadoc) 074 * 075 * @see org.graphstream.stream.file.FileSourceXML#nextEvents() 076 */ 077 public boolean nextEvents() throws IOException { 078 return false; 079 } 080 081 /* 082 * (non-Javadoc) 083 * 084 * @see org.graphstream.stream.file.FileSourceXML#beforeEndDocument() 085 */ 086 protected void beforeEndDocument() { 087 parser = null; 088 } 089 090 @SuppressWarnings("unused") 091 private class Attribute implements GEXFConstants { 092 final String id; 093 final String title; 094 final AttributeType type; 095 Object def; 096 String options; 097 098 Attribute(String id, String title, AttributeType type) { 099 this.id = id; 100 this.title = title; 101 this.type = type; 102 } 103 104 Object getValue(String value) { 105 Object r; 106 107 switch (type) { 108 case INTEGER: 109 r = Integer.valueOf(value); 110 break; 111 case LONG: 112 r = Long.valueOf(value); 113 break; 114 case FLOAT: 115 r = Float.valueOf(value); 116 break; 117 case DOUBLE: 118 r = Double.valueOf(value); 119 break; 120 case BOOLEAN: 121 r = Boolean.valueOf(value); 122 break; 123 case LISTSTRING: 124 String[] list = value.split("\\|"); 125 126 boolean isDouble = true; 127 128 for (int i = 0; i < list.length; i++) 129 isDouble = isDouble && IS_DOUBLE.matcher(list[i]).matches(); 130 131 if (isDouble) { 132 double[] dlist = new double[list.length]; 133 134 for (int i = 0; i < list.length; i++) 135 dlist[i] = Double.parseDouble(list[i]); 136 137 r = dlist; 138 } else 139 r = list; 140 141 break; 142 case ANYURI: 143 try { 144 r = new URI(value); 145 } catch (URISyntaxException e) { 146 throw new IllegalArgumentException(e); 147 } 148 break; 149 default: 150 r = value; 151 } 152 153 return r; 154 } 155 156 void setDefault(String value) { 157 this.def = getValue(value); 158 } 159 160 void setOptions(String options) { 161 this.options = options; 162 } 163 } 164 165 private class GEXFParser extends Parser implements GEXFConstants { 166 EdgeType defaultEdgeType; 167 TimeFormatType timeFormat; 168 HashMap<String, Attribute> nodeAttributesDefinition; 169 HashMap<String, Attribute> edgeAttributesDefinition; 170 171 GEXFParser() { 172 defaultEdgeType = EdgeType.UNDIRECTED; 173 timeFormat = TimeFormatType.INTEGER; 174 nodeAttributesDefinition = new HashMap<String, Attribute>(); 175 edgeAttributesDefinition = new HashMap<String, Attribute>(); 176 } 177 178 @SuppressWarnings("unused") 179 private long getTime(String time) { 180 long t = 0; 181 182 switch (timeFormat) { 183 case INTEGER: 184 t = Integer.valueOf(time); 185 break; 186 case DOUBLE: 187 // TODO 188 break; 189 case DATE: 190 // TODO 191 break; 192 case DATETIME: 193 // TODO 194 break; 195 } 196 197 return t; 198 } 199 200 /** 201 * name : GEXF attributes : GEXFAttribute structure : META ? GRAPH 202 */ 203 private void __gexf() throws IOException, XMLStreamException { 204 XMLEvent e; 205 206 e = getNextEvent(); 207 checkValid(e, XMLEvent.START_ELEMENT, "gexf"); 208 209 e = getNextEvent(); 210 211 if (isEvent(e, XMLEvent.START_ELEMENT, "meta")) { 212 pushback(e); 213 __meta(); 214 } else 215 pushback(e); 216 217 __graph(); 218 219 e = getNextEvent(); 220 checkValid(e, XMLEvent.END_ELEMENT, "gexf"); 221 } 222 223 /** 224 * name : META attributes : METAttribute structure : ( CREATOR | 225 * KEYWORDS | DESCRIPTION )* 226 */ 227 private void __meta() throws IOException, XMLStreamException { 228 EnumMap<METAAttribute, String> attributes; 229 XMLEvent e; 230 231 e = getNextEvent(); 232 checkValid(e, XMLEvent.START_ELEMENT, "meta"); 233 234 attributes = getAttributes(METAAttribute.class, e.asStartElement()); 235 236 if (attributes.containsKey(METAAttribute.LASTMODIFIEDDATE)) 237 sendGraphAttributeAdded(sourceId, "lastmodifieddate", 238 attributes.get(METAAttribute.LASTMODIFIEDDATE)); 239 240 e = getNextEvent(); 241 242 while (!isEvent(e, XMLEvent.END_ELEMENT, "meta")) { 243 try { 244 String str; 245 Balise b = Balise.valueOf(toConstantName(e.asStartElement() 246 .getName().getLocalPart())); 247 248 pushback(e); 249 250 switch (b) { 251 case CREATOR: 252 str = __creator(); 253 sendGraphAttributeAdded(sourceId, "creator", str); 254 break; 255 case KEYWORDS: 256 str = __keywords(); 257 sendGraphAttributeAdded(sourceId, "keywords", str); 258 break; 259 case DESCRIPTION: 260 str = __description(); 261 sendGraphAttributeAdded(sourceId, "description", str); 262 break; 263 default: 264 throw newParseError(e, 265 "meta children should be one of 'creator','keywords' or 'description'"); 266 } 267 } catch (IllegalArgumentException ex) { 268 throw newParseError(e, "unknown element '%s'", e 269 .asStartElement().getName().getLocalPart()); 270 } 271 272 e = getNextEvent(); 273 } 274 275 checkValid(e, XMLEvent.END_ELEMENT, "meta"); 276 } 277 278 /** 279 * name : CREATOR attributes : structure : string 280 */ 281 private String __creator() throws IOException, XMLStreamException { 282 String creator; 283 XMLEvent e; 284 285 e = getNextEvent(); 286 checkValid(e, XMLEvent.START_ELEMENT, "creator"); 287 288 creator = __characters(); 289 290 e = getNextEvent(); 291 checkValid(e, XMLEvent.END_ELEMENT, "creator"); 292 293 return creator; 294 } 295 296 /** 297 * name : KEYWORDS attributes : structure : string 298 */ 299 private String __keywords() throws IOException, XMLStreamException { 300 String keywords; 301 XMLEvent e; 302 303 e = getNextEvent(); 304 checkValid(e, XMLEvent.START_ELEMENT, "keywords"); 305 306 keywords = __characters(); 307 308 e = getNextEvent(); 309 checkValid(e, XMLEvent.END_ELEMENT, "keywords"); 310 311 return keywords; 312 } 313 314 /** 315 * <pre> 316 * name : DESCRIPTION 317 * attributes : 318 * structure : string 319 * </pre> 320 */ 321 private String __description() throws IOException, XMLStreamException { 322 String description; 323 XMLEvent e; 324 325 e = getNextEvent(); 326 checkValid(e, XMLEvent.START_ELEMENT, "description"); 327 328 description = __characters(); 329 330 e = getNextEvent(); 331 checkValid(e, XMLEvent.END_ELEMENT, "description"); 332 333 return description; 334 } 335 336 /** 337 * <pre> 338 * name : GRAPH 339 * attributes : GRAPHAttribute 340 * structure : ATTRIBUTES * ( NODES | EDGES )* 341 * </pre> 342 */ 343 private void __graph() throws IOException, XMLStreamException { 344 XMLEvent e; 345 EnumMap<GRAPHAttribute, String> attributes; 346 347 e = getNextEvent(); 348 checkValid(e, XMLEvent.START_ELEMENT, "graph"); 349 350 attributes = getAttributes(GRAPHAttribute.class, e.asStartElement()); 351 352 if (attributes.containsKey(GRAPHAttribute.DEFAULTEDGETYPE)) { 353 try { 354 defaultEdgeType = EdgeType 355 .valueOf(toConstantName(attributes 356 .get(GRAPHAttribute.DEFAULTEDGETYPE))); 357 } catch (IllegalArgumentException ex) { 358 throw newParseError(e, 359 "'defaultedgetype' value should be one of 'directed', 'undirected' or 'mutual'"); 360 } 361 } 362 363 if (attributes.containsKey(GRAPHAttribute.TIMEFORMAT)) { 364 try { 365 timeFormat = TimeFormatType 366 .valueOf(toConstantName(attributes 367 .get(GRAPHAttribute.TIMEFORMAT))); 368 } catch (IllegalArgumentException ex) { 369 throw newParseError(e, 370 "'timeformat' value should be one of 'integer', 'double', 'date' or 'datetime'"); 371 } 372 } 373 374 e = getNextEvent(); 375 376 while (isEvent(e, XMLEvent.START_ELEMENT, "attributes")) { 377 pushback(e); 378 379 __attributes(); 380 e = getNextEvent(); 381 } 382 383 while (isEvent(e, XMLEvent.START_ELEMENT, "nodes") 384 || isEvent(e, XMLEvent.START_ELEMENT, "edges")) { 385 if (isEvent(e, XMLEvent.START_ELEMENT, "nodes")) { 386 pushback(e); 387 __nodes(); 388 } else { 389 pushback(e); 390 __edges(); 391 } 392 393 e = getNextEvent(); 394 } 395 396 checkValid(e, XMLEvent.END_ELEMENT, "graph"); 397 } 398 399 /** 400 * <pre> 401 * name : ATTRIBUTES 402 * attributes : ATTRIBUTESAttributes { CLASS!, MODE, START, STARTOPEN, END, ENDOPEN } 403 * structure : ATTRIBUTE * 404 * </pre> 405 */ 406 private void __attributes() throws IOException, XMLStreamException { 407 XMLEvent e; 408 EnumMap<ATTRIBUTESAttribute, String> attributes; 409 Attribute a; 410 ClassType type; 411 HashMap<String, Attribute> attr; 412 413 e = getNextEvent(); 414 checkValid(e, XMLEvent.START_ELEMENT, "attributes"); 415 416 attributes = getAttributes(ATTRIBUTESAttribute.class, 417 e.asStartElement()); 418 419 checkRequiredAttributes(e, attributes, ATTRIBUTESAttribute.CLASS); 420 421 try { 422 type = ClassType.valueOf(toConstantName(attributes 423 .get(ATTRIBUTESAttribute.CLASS))); 424 } catch (IllegalArgumentException ex) { 425 throw newParseError(e, 426 "'class' value shoudl be one of 'node' or 'edge'"); 427 } 428 429 if (type == ClassType.NODE) 430 attr = nodeAttributesDefinition; 431 else 432 attr = edgeAttributesDefinition; 433 434 e = getNextEvent(); 435 436 while (isEvent(e, XMLEvent.START_ELEMENT, "attribute")) { 437 pushback(e); 438 439 a = __attribute(); 440 attr.put(a.id, a); 441 e = getNextEvent(); 442 } 443 444 checkValid(e, XMLEvent.END_ELEMENT, "attributes"); 445 } 446 447 /** 448 * <pre> 449 * name : ATTRIBUTE 450 * attributes : ATTRIBUTEAttribute { ID, TITLE, TYPE } 451 * structure : ( DEFAULT | OPTIONS ) * 452 * </pre> 453 */ 454 private Attribute __attribute() throws IOException, XMLStreamException { 455 XMLEvent e; 456 EnumMap<ATTRIBUTEAttribute, String> attributes; 457 String id, title; 458 AttributeType type; 459 Attribute theAttribute; 460 461 e = getNextEvent(); 462 checkValid(e, XMLEvent.START_ELEMENT, "attribute"); 463 464 attributes = getAttributes(ATTRIBUTEAttribute.class, 465 e.asStartElement()); 466 467 checkRequiredAttributes(e, attributes, ATTRIBUTEAttribute.ID, 468 ATTRIBUTEAttribute.TITLE, ATTRIBUTEAttribute.TYPE); 469 470 id = attributes.get(ATTRIBUTEAttribute.ID); 471 title = attributes.get(ATTRIBUTEAttribute.TITLE); 472 473 try { 474 type = AttributeType.valueOf(toConstantName(attributes 475 .get(ATTRIBUTEAttribute.TYPE))); 476 } catch (IllegalArgumentException ex) { 477 throw newParseError( 478 e, 479 "'type' of attribute should be one of 'integer', 'long', 'float, 'double', 'string', 'liststring', 'anyURI' or 'boolean'"); 480 } 481 482 theAttribute = new Attribute(id, title, type); 483 484 e = getNextEvent(); 485 486 while (!isEvent(e, XMLEvent.END_ELEMENT, "attribute")) { 487 try { 488 Balise b = Balise.valueOf(toConstantName(e.asStartElement() 489 .getName().getLocalPart())); 490 491 pushback(e); 492 493 switch (b) { 494 case DEFAULT: 495 try { 496 theAttribute.setDefault(__default()); 497 } catch (Exception invalid) { 498 throw newParseError(e, "invalid 'default' value"); 499 } 500 501 break; 502 case OPTIONS: 503 theAttribute.setOptions(__options()); 504 break; 505 default: 506 throw newParseError(e, 507 "attribute children should be one of 'default' or 'options'"); 508 } 509 } catch (IllegalArgumentException ex) { 510 throw newParseError(e, "unknown element '%s'", e 511 .asStartElement().getName().getLocalPart()); 512 } 513 514 e = getNextEvent(); 515 } 516 517 checkValid(e, XMLEvent.END_ELEMENT, "attribute"); 518 519 return theAttribute; 520 } 521 522 /** 523 * <pre> 524 * name : DEFAULT 525 * attributes : 526 * structure : string 527 * </pre> 528 */ 529 private String __default() throws IOException, XMLStreamException { 530 String def; 531 XMLEvent e; 532 533 e = getNextEvent(); 534 checkValid(e, XMLEvent.START_ELEMENT, "default"); 535 536 def = __characters(); 537 538 e = getNextEvent(); 539 checkValid(e, XMLEvent.END_ELEMENT, "default"); 540 541 return def; 542 } 543 544 /** 545 * <pre> 546 * name : OPTIONS 547 * attributes : 548 * structure : string 549 * </pre> 550 */ 551 private String __options() throws IOException, XMLStreamException { 552 String options; 553 XMLEvent e; 554 555 e = getNextEvent(); 556 checkValid(e, XMLEvent.START_ELEMENT, "options"); 557 558 options = __characters(); 559 560 e = getNextEvent(); 561 checkValid(e, XMLEvent.END_ELEMENT, "options"); 562 563 return options; 564 } 565 566 /** 567 * <pre> 568 * name : NODES 569 * attributes : NODESAttribute { 'count' } 570 * structure : NODE * 571 * </pre> 572 */ 573 private void __nodes() throws IOException, XMLStreamException { 574 XMLEvent e; 575 576 e = getNextEvent(); 577 checkValid(e, XMLEvent.START_ELEMENT, "nodes"); 578 579 e = getNextEvent(); 580 581 while (isEvent(e, XMLEvent.START_ELEMENT, "node")) { 582 pushback(e); 583 584 __node(); 585 e = getNextEvent(); 586 } 587 588 checkValid(e, XMLEvent.END_ELEMENT, "nodes"); 589 590 } 591 592 /** 593 * <pre> 594 * name : NODE 595 * attributes : NODEAttribute { 'pid', 'id', 'label', 'start', 'startopen', 'end', 'endopen' } 596 * structure : ( ATTVALUES | SPELLS | ( NODES | EDGES ) | PARENTS | ( COLOR | POSITION | SIZE | NODESHAPE ) ) * 597 * </pre> 598 */ 599 private void __node() throws IOException, XMLStreamException { 600 XMLEvent e; 601 EnumMap<NODEAttribute, String> attributes; 602 String id; 603 HashSet<String> defined = new HashSet<String>(); 604 605 e = getNextEvent(); 606 checkValid(e, XMLEvent.START_ELEMENT, "node"); 607 608 attributes = getAttributes(NODEAttribute.class, e.asStartElement()); 609 610 checkRequiredAttributes(e, attributes, NODEAttribute.ID); 611 612 id = attributes.get(NODEAttribute.ID); 613 sendNodeAdded(sourceId, id); 614 615 if (attributes.containsKey(NODEAttribute.LABEL)) 616 sendNodeAttributeAdded(sourceId, id, "label", 617 attributes.get(NODEAttribute.LABEL)); 618 619 e = getNextEvent(); 620 621 while (!isEvent(e, XMLEvent.END_ELEMENT, "node")) { 622 try { 623 Balise b = Balise.valueOf(toConstantName(e.asStartElement() 624 .getName().getLocalPart())); 625 626 pushback(e); 627 628 switch (b) { 629 case ATTVALUES: 630 defined.addAll(__attvalues(ClassType.NODE, id)); 631 break; 632 case COLOR: 633 __color(ClassType.NODE, id); 634 break; 635 case POSITION: 636 __position(id); 637 break; 638 case SIZE: 639 __size(id); 640 break; 641 case SHAPE: 642 __node_shape(id); 643 break; 644 case SPELLS: 645 __spells(); 646 break; 647 case NODES: 648 __nodes(); 649 break; 650 case EDGES: 651 __edges(); 652 break; 653 case PARENTS: 654 __parents(id); 655 break; 656 default: 657 throw newParseError( 658 e, 659 "attribute children should be one of 'attvalues', 'color', 'position', 'size', shape', 'spells', 'nodes, 'edges' or 'parents'"); 660 } 661 } catch (IllegalArgumentException ex) { 662 throw newParseError(e, "unknown element '%s'", e 663 .asStartElement().getName().getLocalPart()); 664 } 665 666 e = getNextEvent(); 667 } 668 669 for (Attribute theAttribute : nodeAttributesDefinition.values()) { 670 if (!defined.contains(theAttribute.id)) { 671 sendNodeAttributeAdded(sourceId, id, theAttribute.title, 672 theAttribute.def); 673 } 674 } 675 676 checkValid(e, XMLEvent.END_ELEMENT, "node"); 677 } 678 679 /** 680 * <pre> 681 * name : ATTVALUES 682 * attributes : 683 * structure : ATTVALUE * 684 * </spell> 685 */ 686 private HashSet<String> __attvalues(ClassType type, String elementId) 687 throws IOException, XMLStreamException { 688 XMLEvent e; 689 HashSet<String> defined = new HashSet<String>(); 690 691 e = getNextEvent(); 692 checkValid(e, XMLEvent.START_ELEMENT, "attvalues"); 693 694 e = getNextEvent(); 695 696 while (isEvent(e, XMLEvent.START_ELEMENT, "attvalue")) { 697 pushback(e); 698 699 defined.add(__attvalue(type, elementId)); 700 e = getNextEvent(); 701 } 702 703 checkValid(e, XMLEvent.END_ELEMENT, "attvalues"); 704 705 return defined; 706 } 707 708 /** 709 * <pre> 710 * name : ATTVALUE 711 * attributes : ATTVALUEAttribute { FOR!, VALUE!, START, STARTOPEN, END, ENDOPEN } 712 * structure : 713 * </pre> 714 */ 715 private String __attvalue(ClassType type, String elementId) 716 throws IOException, XMLStreamException { 717 XMLEvent e; 718 EnumMap<ATTVALUEAttribute, String> attributes; 719 Attribute theAttribute; 720 Object value; 721 722 e = getNextEvent(); 723 checkValid(e, XMLEvent.START_ELEMENT, "attvalue"); 724 725 attributes = getAttributes(ATTVALUEAttribute.class, 726 e.asStartElement()); 727 728 checkRequiredAttributes(e, attributes, ATTVALUEAttribute.FOR, 729 ATTVALUEAttribute.VALUE); 730 731 if (type == ClassType.NODE) 732 theAttribute = nodeAttributesDefinition.get(attributes 733 .get(ATTVALUEAttribute.FOR)); 734 else 735 theAttribute = edgeAttributesDefinition.get(attributes 736 .get(ATTVALUEAttribute.FOR)); 737 738 if (theAttribute == null) 739 throw newParseError(e, "undefined attribute \"%s\"", 740 attributes.get(ATTVALUEAttribute.FOR)); 741 742 try { 743 value = theAttribute.getValue(attributes 744 .get(ATTVALUEAttribute.VALUE)); 745 } catch (Exception ex) { 746 throw newParseError(e, "invalid 'value' value"); 747 } 748 749 switch (type) { 750 case NODE: 751 sendNodeAttributeAdded(sourceId, elementId, theAttribute.title, 752 value); 753 break; 754 case EDGE: 755 sendEdgeAttributeAdded(sourceId, elementId, theAttribute.title, 756 value); 757 break; 758 } 759 760 e = getNextEvent(); 761 checkValid(e, XMLEvent.END_ELEMENT, "attvalue"); 762 763 return theAttribute.id; 764 } 765 766 /** 767 * <pre> 768 * name : SPELLS 769 * attributes : 770 * structure : SPELL + 771 * </pre> 772 */ 773 private void __spells() throws IOException, XMLStreamException { 774 XMLEvent e; 775 776 e = getNextEvent(); 777 checkValid(e, XMLEvent.START_ELEMENT, "spells"); 778 779 do { 780 __spell(); 781 e = getNextEvent(); 782 } while (isEvent(e, XMLEvent.START_ELEMENT, "spell")); 783 784 checkValid(e, XMLEvent.END_ELEMENT, "spells"); 785 } 786 787 /** 788 * <pre> 789 * name : SPELL 790 * attributes : SPELLAttribute 791 * structure : 792 * </pre> 793 */ 794 @SuppressWarnings("unused") 795 private void __spell() throws IOException, XMLStreamException { 796 XMLEvent e; 797 EnumMap<SPELLAttribute, String> attributes; 798 799 e = getNextEvent(); 800 checkValid(e, XMLEvent.START_ELEMENT, "spell"); 801 802 attributes = getAttributes(SPELLAttribute.class, e.asStartElement()); 803 804 // TODO Handle spell 805 806 e = getNextEvent(); 807 checkValid(e, XMLEvent.END_ELEMENT, "spell"); 808 } 809 810 /** 811 * <pre> 812 * name : PARENTS 813 * attributes : 814 * structure : PARENT * 815 * </pre> 816 */ 817 private void __parents(String nodeId) throws IOException, 818 XMLStreamException { 819 XMLEvent e; 820 821 e = getNextEvent(); 822 checkValid(e, XMLEvent.START_ELEMENT, "parents"); 823 824 e = getNextEvent(); 825 826 while (isEvent(e, XMLEvent.START_ELEMENT, "parent")) { 827 __parent(nodeId); 828 e = getNextEvent(); 829 } 830 831 checkValid(e, XMLEvent.END_ELEMENT, "parents"); 832 } 833 834 /** 835 * <pre> 836 * name : PARENT 837 * attributes : PARENTAttribute { FOR! } 838 * structure : 839 * </pre> 840 */ 841 private void __parent(String nodeId) throws IOException, 842 XMLStreamException { 843 XMLEvent e; 844 EnumMap<PARENTAttribute, String> attributes; 845 846 e = getNextEvent(); 847 checkValid(e, XMLEvent.START_ELEMENT, "parent"); 848 849 attributes = getAttributes(PARENTAttribute.class, 850 e.asStartElement()); 851 852 checkRequiredAttributes(e, attributes, PARENTAttribute.FOR); 853 sendNodeAttributeAdded(sourceId, 854 attributes.get(PARENTAttribute.FOR), "parent", nodeId); 855 856 e = getNextEvent(); 857 checkValid(e, XMLEvent.END_ELEMENT, "parent"); 858 } 859 860 /** 861 * <pre> 862 * name : COLOR 863 * attributes : COLORAttribute { R!, G!, B!, A, START, STARTOPEN, END, ENDOPEN } 864 * structure : SPELLS ? 865 * </pre> 866 */ 867 private void __color(ClassType type, String id) throws IOException, 868 XMLStreamException { 869 XMLEvent e; 870 EnumMap<COLORAttribute, String> attributes; 871 Color color; 872 int r, g, b, a = 255; 873 874 e = getNextEvent(); 875 checkValid(e, XMLEvent.START_ELEMENT, "color"); 876 877 attributes = getAttributes(COLORAttribute.class, e.asStartElement()); 878 879 checkRequiredAttributes(e, attributes, COLORAttribute.R, 880 COLORAttribute.G, COLORAttribute.B); 881 882 r = Integer.valueOf(attributes.get(COLORAttribute.R)); 883 g = Integer.valueOf(attributes.get(COLORAttribute.G)); 884 b = Integer.valueOf(attributes.get(COLORAttribute.B)); 885 886 if (attributes.containsKey(COLORAttribute.A)) 887 a = Integer.valueOf(attributes.get(COLORAttribute.A)); 888 889 color = new Color(r, g, b, a); 890 891 switch (type) { 892 case NODE: 893 sendNodeAttributeAdded(sourceId, id, "ui.color", color); 894 break; 895 case EDGE: 896 sendEdgeAttributeAdded(sourceId, id, "ui.color", color); 897 break; 898 } 899 900 e = getNextEvent(); 901 902 if (isEvent(e, XMLEvent.START_ELEMENT, "spells")) { 903 pushback(e); 904 905 __spells(); 906 e = getNextEvent(); 907 } 908 909 checkValid(e, XMLEvent.END_ELEMENT, "color"); 910 } 911 912 /** 913 * <pre> 914 * name : POSITION 915 * attributes : POSITIONAttribute { X!, Y!, Z!, START, STARTOPEN, END, ENDOPEN } 916 * structure : SPELLS ? 917 * </pre> 918 */ 919 private void __position(String nodeId) throws IOException, 920 XMLStreamException { 921 XMLEvent e; 922 EnumMap<POSITIONAttribute, String> attributes; 923 double[] xyz = { 0, 0, 0 }; 924 925 e = getNextEvent(); 926 checkValid(e, XMLEvent.START_ELEMENT, "position"); 927 928 attributes = getAttributes(POSITIONAttribute.class, 929 e.asStartElement()); 930 931 checkRequiredAttributes(e, attributes, POSITIONAttribute.X, 932 POSITIONAttribute.Y, POSITIONAttribute.Z); 933 934 xyz[0] = Double.valueOf(attributes.get(POSITIONAttribute.X)); 935 xyz[1] = Double.valueOf(attributes.get(POSITIONAttribute.Y)); 936 xyz[2] = Double.valueOf(attributes.get(POSITIONAttribute.Z)); 937 938 sendNodeAttributeAdded(sourceId, nodeId, "xyz", xyz); 939 940 e = getNextEvent(); 941 942 if (isEvent(e, XMLEvent.START_ELEMENT, "spells")) { 943 pushback(e); 944 945 __spells(); 946 e = getNextEvent(); 947 } 948 949 checkValid(e, XMLEvent.END_ELEMENT, "position"); 950 } 951 952 /** 953 * <pre> 954 * name : SIZE 955 * attributes : SIZEAttribute { VALUE!, START, STARTOPEN, END, ENDOPEN } 956 * structure : SPELLS ? 957 * </pre> 958 */ 959 private void __size(String nodeId) throws IOException, 960 XMLStreamException { 961 XMLEvent e; 962 EnumMap<SIZEAttribute, String> attributes; 963 double value; 964 965 e = getNextEvent(); 966 checkValid(e, XMLEvent.START_ELEMENT, "size"); 967 968 attributes = getAttributes(SIZEAttribute.class, e.asStartElement()); 969 970 checkRequiredAttributes(e, attributes, SIZEAttribute.VALUE); 971 972 value = Double.valueOf(attributes.get(SIZEAttribute.VALUE)); 973 974 sendNodeAttributeAdded(sourceId, nodeId, "ui.size", value); 975 976 e = getNextEvent(); 977 978 if (isEvent(e, XMLEvent.START_ELEMENT, "spells")) { 979 pushback(e); 980 981 __spells(); 982 e = getNextEvent(); 983 } 984 985 checkValid(e, XMLEvent.END_ELEMENT, "size"); 986 } 987 988 /** 989 * <pre> 990 * name : NODESHAPE 991 * attributes : NODESHAPEAttributes { VALUE!, URI, START, STARTOPEN, END, ENDOPEN } 992 * structure : SPELLS ? 993 * </pre> 994 */ 995 private void __node_shape(String nodeId) throws IOException, 996 XMLStreamException { 997 XMLEvent e; 998 EnumMap<NODESHAPEAttribute, String> attributes; 999 NodeShapeType type; 1000 String uri; 1001 1002 e = getNextEvent(); 1003 checkValid(e, XMLEvent.START_ELEMENT, "shape"); 1004 1005 attributes = getAttributes(NODESHAPEAttribute.class, 1006 e.asStartElement()); 1007 1008 checkRequiredAttributes(e, attributes, NODESHAPEAttribute.VALUE); 1009 1010 try { 1011 type = NodeShapeType.valueOf(toConstantName(attributes 1012 .get(NODESHAPEAttribute.VALUE))); 1013 } catch (IllegalArgumentException ex) { 1014 throw newParseError(e, 1015 "'value' should be one of 'disc', 'diamond', 'triangle', 'square' or 'image'"); 1016 } 1017 1018 switch (type) { 1019 case IMAGE: 1020 if (!attributes.containsKey(NODESHAPEAttribute.URI)) 1021 throw newParseError(e, 1022 "'image' shape type needs 'uri' attribute"); 1023 1024 uri = attributes.get(NODESHAPEAttribute.URI); 1025 sendNodeAttributeAdded( 1026 sourceId, 1027 nodeId, 1028 "ui.style", 1029 String.format( 1030 "fill-mode: image-scaled; fill-image: url('%s');", 1031 uri)); 1032 1033 break; 1034 default: 1035 sendNodeAttributeAdded(sourceId, nodeId, "ui.style", 1036 String.format("shape: %s;", type.name().toLowerCase())); 1037 } 1038 1039 e = getNextEvent(); 1040 1041 if (isEvent(e, XMLEvent.START_ELEMENT, "spells")) { 1042 pushback(e); 1043 1044 __spells(); 1045 e = getNextEvent(); 1046 } 1047 1048 checkValid(e, XMLEvent.END_ELEMENT, "shape"); 1049 } 1050 1051 /** 1052 * <pre> 1053 * name : EDGES 1054 * attributes : EDGESAttribute { 'count' } 1055 * structure : EDGE * 1056 * </pre> 1057 */ 1058 private void __edges() throws IOException, XMLStreamException { 1059 XMLEvent e; 1060 1061 e = getNextEvent(); 1062 checkValid(e, XMLEvent.START_ELEMENT, "edges"); 1063 1064 e = getNextEvent(); 1065 1066 while (isEvent(e, XMLEvent.START_ELEMENT, "edge")) { 1067 pushback(e); 1068 1069 __edge(); 1070 e = getNextEvent(); 1071 } 1072 1073 checkValid(e, XMLEvent.END_ELEMENT, "edges"); 1074 } 1075 1076 /** 1077 * <pre> 1078 * name : EDGE 1079 * attributes : EDGEAttribute { START, STARTOPEN, END, ENDOPEN, ID!, TYPE, LABEL, SOURCE!, TARGET!, WEIGHT } 1080 * structure : ( ATTVALUES | SPELLS | ( COLOR | THICKNESS | EDGESHAPE ) ) * 1081 * </pre> 1082 */ 1083 private void __edge() throws IOException, XMLStreamException { 1084 XMLEvent e; 1085 EnumMap<EDGEAttribute, String> attributes; 1086 String id, source, target; 1087 EdgeType type = defaultEdgeType; 1088 HashSet<String> defined = new HashSet<String>(); 1089 1090 e = getNextEvent(); 1091 checkValid(e, XMLEvent.START_ELEMENT, "edge"); 1092 1093 attributes = getAttributes(EDGEAttribute.class, e.asStartElement()); 1094 1095 checkRequiredAttributes(e, attributes, EDGEAttribute.ID, 1096 EDGEAttribute.SOURCE, EDGEAttribute.TARGET); 1097 1098 id = attributes.get(EDGEAttribute.ID); 1099 source = attributes.get(EDGEAttribute.SOURCE); 1100 target = attributes.get(EDGEAttribute.TARGET); 1101 1102 if (attributes.containsKey(EDGEAttribute.TYPE)) { 1103 try { 1104 type = EdgeType.valueOf(toConstantName(attributes 1105 .get(EDGEAttribute.TYPE))); 1106 } catch (IllegalArgumentException ex) { 1107 throw newParseError(e, 1108 "edge type should be one of 'undirected', 'undirected' or 'mutual'"); 1109 } 1110 } 1111 1112 switch (type) { 1113 case DIRECTED: 1114 sendEdgeAdded(sourceId, id, source, target, true); 1115 break; 1116 case MUTUAL: 1117 case UNDIRECTED: 1118 sendEdgeAdded(sourceId, id, source, target, false); 1119 break; 1120 } 1121 1122 if (attributes.containsKey(EDGEAttribute.LABEL)) 1123 sendEdgeAttributeAdded(sourceId, id, "ui.label", 1124 attributes.get(EDGEAttribute.LABEL)); 1125 1126 if (attributes.containsKey(EDGEAttribute.WEIGHT)) { 1127 try { 1128 double d = Double.valueOf(attributes 1129 .get(EDGEAttribute.WEIGHT)); 1130 sendEdgeAttributeAdded(sourceId, id, "weight", d); 1131 } catch (NumberFormatException ex) { 1132 throw newParseError(e, 1133 "'weight' attribute of edge should be a real"); 1134 } 1135 } 1136 1137 e = getNextEvent(); 1138 1139 while (!isEvent(e, XMLEvent.END_ELEMENT, "edge")) { 1140 try { 1141 Balise b = Balise.valueOf(toConstantName(e.asStartElement() 1142 .getName().getLocalPart())); 1143 1144 pushback(e); 1145 1146 switch (b) { 1147 case ATTVALUES: 1148 defined.addAll(__attvalues(ClassType.EDGE, id)); 1149 break; 1150 case SPELLS: 1151 __spells(); 1152 break; 1153 case COLOR: 1154 __color(ClassType.EDGE, id); 1155 break; 1156 case THICKNESS: 1157 __thickness(id); 1158 break; 1159 case SHAPE: 1160 __edge_shape(id); 1161 break; 1162 default: 1163 throw newParseError( 1164 e, 1165 "edge children should be one of 'attvalues', 'color', 'thicknes', 'shape' or 'spells'"); 1166 } 1167 } catch (IllegalArgumentException ex) { 1168 throw newParseError(e, "unknown tag '%s'", e 1169 .asStartElement().getName().getLocalPart()); 1170 } 1171 1172 e = getNextEvent(); 1173 } 1174 1175 for (String key : edgeAttributesDefinition.keySet()) { 1176 if (!defined.contains(key)) 1177 sendEdgeAttributeAdded(sourceId, id, key, 1178 edgeAttributesDefinition.get(key).def); 1179 } 1180 1181 checkValid(e, XMLEvent.END_ELEMENT, "edge"); 1182 } 1183 1184 /** 1185 * <pre> 1186 * name : EDGESHAPE 1187 * attributes : EDGESHAPEAttributes { VALUE!, START, STARTOPEN, END, ENDOPEN } 1188 * structure : SPELLS ? 1189 * </pre> 1190 */ 1191 @SuppressWarnings("unused") 1192 private void __edge_shape(String edgeId) throws IOException, 1193 XMLStreamException { 1194 XMLEvent e; 1195 EnumMap<EDGESHAPEAttribute, String> attributes; 1196 EdgeShapeType type; 1197 1198 e = getNextEvent(); 1199 checkValid(e, XMLEvent.START_ELEMENT, "shape"); 1200 1201 attributes = getAttributes(EDGESHAPEAttribute.class, 1202 e.asStartElement()); 1203 checkRequiredAttributes(e, attributes, EDGESHAPEAttribute.VALUE); 1204 1205 try { 1206 type = EdgeShapeType.valueOf(toConstantName(attributes 1207 .get(EDGESHAPEAttribute.VALUE))); 1208 } catch (IllegalArgumentException ex) { 1209 throw newParseError(e, 1210 "'value' of shape should be one of 'solid', 'dotted', 'dashed' or 'double'"); 1211 } 1212 1213 // TODO Handle shape of edges 1214 1215 e = getNextEvent(); 1216 1217 if (isEvent(e, XMLEvent.START_ELEMENT, "spells")) { 1218 pushback(e); 1219 1220 __spells(); 1221 e = getNextEvent(); 1222 } 1223 1224 checkValid(e, XMLEvent.END_ELEMENT, "shape"); 1225 } 1226 1227 /** 1228 * <pre> 1229 * name : THICKNESS 1230 * attributes : THICKNESSAttribute { VALUE!, START, STARTOPEN, END, ENDOPEN } 1231 * structure : SPELLS ? 1232 * </pre> 1233 */ 1234 private void __thickness(String edgeId) throws IOException, 1235 XMLStreamException { 1236 XMLEvent e; 1237 EnumMap<THICKNESSAttribute, String> attributes; 1238 1239 e = getNextEvent(); 1240 checkValid(e, XMLEvent.START_ELEMENT, "thickness"); 1241 1242 attributes = getAttributes(THICKNESSAttribute.class, 1243 e.asStartElement()); 1244 1245 checkRequiredAttributes(e, attributes, THICKNESSAttribute.VALUE); 1246 1247 e = getNextEvent(); 1248 1249 if (isEvent(e, XMLEvent.START_ELEMENT, "spells")) { 1250 pushback(e); 1251 1252 __spells(); 1253 e = getNextEvent(); 1254 } 1255 1256 checkValid(e, XMLEvent.END_ELEMENT, "thickness"); 1257 } 1258 } 1259 1260 public static interface GEXFConstants { 1261 public static enum Balise { 1262 GEXF, GRAPH, META, CREATOR, KEYWORDS, DESCRIPTION, NODES, NODE, EDGES, EDGE, COLOR, POSITION, SIZE, SHAPE, THICKNESS, DEFAULT, OPTIONS, ATTVALUES, PARENTS, SPELLS 1263 } 1264 1265 public static enum GEXFAttribute { 1266 XMLNS, VERSION 1267 } 1268 1269 public static enum METAAttribute { 1270 LASTMODIFIEDDATE 1271 } 1272 1273 public static enum GRAPHAttribute { 1274 TIMEFORMAT, START, STARTOPEN, END, ENDOPEN, DEFAULTEDGETYPE, IDTYPE, MODE 1275 } 1276 1277 public static enum ATTRIBUTESAttribute { 1278 CLASS, MODE, START, STARTOPEN, END, ENDOPEN 1279 } 1280 1281 public static enum ATTRIBUTEAttribute { 1282 ID, TITLE, TYPE 1283 } 1284 1285 public static enum NODESAttribute { 1286 COUNT 1287 } 1288 1289 public static enum NODEAttribute { 1290 START, STARTOPEN, END, ENDOPEN, PID, ID, LABEL 1291 } 1292 1293 public static enum ATTVALUEAttribute { 1294 FOR, VALUE, START, STARTOPEN, END, ENDOPEN 1295 } 1296 1297 public static enum PARENTAttribute { 1298 FOR 1299 } 1300 1301 public static enum EDGESAttribute { 1302 COUNT 1303 } 1304 1305 public static enum SPELLAttribute { 1306 START, STARTOPEN, END, ENDOPEN 1307 } 1308 1309 public static enum COLORAttribute { 1310 R, G, B, A, START, STARTOPEN, END, ENDOPEN 1311 } 1312 1313 public static enum POSITIONAttribute { 1314 X, Y, Z, START, STARTOPEN, END, ENDOPEN 1315 } 1316 1317 public static enum SIZEAttribute { 1318 VALUE, START, STARTOPEN, END, ENDOPEN 1319 } 1320 1321 public static enum NODESHAPEAttribute { 1322 VALUE, URI, START, STARTOPEN, END, ENDOPEN 1323 } 1324 1325 public static enum EDGEAttribute { 1326 START, STARTOPEN, END, ENDOPEN, ID, TYPE, LABEL, SOURCE, TARGET, WEIGHT 1327 } 1328 1329 public static enum THICKNESSAttribute { 1330 VALUE, START, STARTOPEN, END, ENDOPEN 1331 } 1332 1333 public static enum EDGESHAPEAttribute { 1334 VALUE, START, STARTOPEN, END, ENDOPEN 1335 } 1336 1337 public static enum IDType { 1338 INTEGER, STRING 1339 } 1340 1341 public static enum ModeType { 1342 STATIC, DYNAMIC 1343 } 1344 1345 public static enum WeightType { 1346 FLOAT 1347 } 1348 1349 public static enum EdgeType { 1350 DIRECTED, UNDIRECTED, MUTUAL 1351 } 1352 1353 public static enum NodeShapeType { 1354 DISC, SQUARE, TRIANGLE, DIAMOND, IMAGE 1355 } 1356 1357 public static enum EdgeShapeType { 1358 SOLID, DOTTED, DASHED, DOUBLE 1359 } 1360 1361 public static enum AttributeType { 1362 INTEGER, LONG, FLOAT, DOUBLE, BOOLEAN, ANYURI, LISTSTRING, STRING 1363 } 1364 1365 public static enum ClassType { 1366 NODE, EDGE 1367 } 1368 1369 public static enum TimeFormatType { 1370 INTEGER, DOUBLE, DATE, DATETIME 1371 } 1372 } 1373}