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.io.FileReader; 035import java.io.IOException; 036import java.io.InputStream; 037import java.io.InputStreamReader; 038import java.io.Reader; 039import java.net.URL; 040import java.util.HashMap; 041import java.util.HashSet; 042import java.util.Iterator; 043import java.util.LinkedList; 044import java.util.Stack; 045 046import javax.xml.stream.FactoryConfigurationError; 047import javax.xml.stream.Location; 048import javax.xml.stream.XMLEventReader; 049import javax.xml.stream.XMLInputFactory; 050import javax.xml.stream.XMLStreamConstants; 051import javax.xml.stream.XMLStreamException; 052import javax.xml.stream.events.Attribute; 053import javax.xml.stream.events.XMLEvent; 054 055import org.graphstream.stream.SourceBase; 056 057/** 058 * GraphML is a comprehensive and easy-to-use file format for graphs. It 059 * consists of a language core to describe the structural properties of a graph 060 * and a flexible extension mechanism to add application-specific data. Its main 061 * features include support of 062 * <ul> 063 * <li>directed, undirected, and mixed graphs,</li> 064 * <li>hypergraphs,</li> 065 * <li>hierarchical graphs,</li> 066 * <li>graphical representations,</li> 067 * <li>references to external data,</li> 068 * <li>application-specific attribute data, and</li> 069 * <li>light-weight parsers.</li> 070 * </ul> 071 * 072 * Unlike many other file formats for graphs, GraphML does not use a custom 073 * syntax. Instead, it is based on XML and hence ideally suited as a common 074 * denominator for all kinds of services generating, archiving, or processing 075 * graphs. 076 * 077 * <a href="http://graphml.graphdrawing.org/index.html">Source</a> 078 */ 079public class FileSourceGraphML extends SourceBase implements FileSource, 080 XMLStreamConstants { 081 082 protected static enum Balise { 083 GRAPHML, GRAPH, NODE, EDGE, HYPEREDGE, DESC, DATA, LOCATOR, PORT, KEY, DEFAULT 084 } 085 086 protected static enum GraphAttribute { 087 ID, EDGEDEFAULT 088 } 089 090 protected static enum LocatorAttribute { 091 XMLNS_XLINK, XLINK_HREF, XLINK_TYPE 092 } 093 094 protected static enum NodeAttribute { 095 ID 096 } 097 098 protected static enum EdgeAttribute { 099 ID, SOURCE, SOURCEPORT, TARGET, TARGETPORT, DIRECTED 100 } 101 102 protected static enum DataAttribute { 103 KEY, ID 104 } 105 106 protected static enum PortAttribute { 107 NAME 108 } 109 110 protected static enum EndPointAttribute { 111 ID, NODE, PORT, TYPE 112 } 113 114 protected static enum EndPointType { 115 IN, OUT, UNDIR 116 } 117 118 protected static enum HyperEdgeAttribute { 119 ID 120 } 121 122 protected static enum KeyAttribute { 123 ID, FOR, ATTR_NAME, ATTR_TYPE 124 } 125 126 protected static enum KeyDomain { 127 GRAPHML, GRAPH, NODE, EDGE, HYPEREDGE, PORT, ENDPOINT, ALL 128 } 129 130 protected static enum KeyAttrType { 131 BOOLEAN, INT, LONG, FLOAT, DOUBLE, STRING 132 } 133 134 protected static class Key { 135 KeyDomain domain; 136 String name; 137 KeyAttrType type; 138 String def = null; 139 140 Key() { 141 domain = KeyDomain.ALL; 142 name = null; 143 type = KeyAttrType.STRING; 144 } 145 146 Object getKeyValue(String value) { 147 if (value == null) 148 return null; 149 150 switch (type) { 151 case STRING: 152 return value; 153 case INT: 154 return Integer.valueOf(value); 155 case LONG: 156 return Long.valueOf(value); 157 case FLOAT: 158 return Float.valueOf(value); 159 case DOUBLE: 160 return Double.valueOf(value); 161 case BOOLEAN: 162 return Boolean.valueOf(value); 163 } 164 165 return value; 166 } 167 168 Object getDefaultValue() { 169 return getKeyValue(def); 170 } 171 } 172 173 protected static class Data { 174 Key key; 175 String id; 176 String value; 177 } 178 179 protected static class Locator { 180 String href; 181 String xlink; 182 String type; 183 184 Locator() { 185 xlink = "http://www.w3.org/TR/2000/PR-xlink-20001220/"; 186 type = "simple"; 187 href = null; 188 } 189 } 190 191 protected static class Port { 192 String name; 193 String desc; 194 195 LinkedList<Data> datas; 196 LinkedList<Port> ports; 197 198 Port() { 199 name = null; 200 desc = null; 201 202 datas = new LinkedList<Data>(); 203 ports = new LinkedList<Port>(); 204 } 205 } 206 207 protected static class EndPoint { 208 String id; 209 String node; 210 String port; 211 String desc; 212 EndPointType type; 213 214 EndPoint() { 215 id = null; 216 node = null; 217 port = null; 218 desc = null; 219 type = EndPointType.UNDIR; 220 } 221 } 222 223 protected XMLEventReader reader; 224 protected HashMap<String, Key> keys; 225 protected LinkedList<Data> datas; 226 protected Stack<XMLEvent> events; 227 protected Stack<String> graphId; 228 protected int graphCounter; 229 230 /** 231 * Build a new source to parse an xml stream in GraphML format. 232 */ 233 public FileSourceGraphML() { 234 events = new Stack<XMLEvent>(); 235 keys = new HashMap<String, Key>(); 236 datas = new LinkedList<Data>(); 237 graphId = new Stack<String>(); 238 graphCounter = 0; 239 sourceId = String.format("<GraphML stream %x>", System.nanoTime()); 240 } 241 242 /* 243 * (non-Javadoc) 244 * 245 * @see org.graphstream.stream.file.FileSource#readAll(java.lang.String) 246 */ 247 public void readAll(String fileName) throws IOException { 248 readAll(new FileReader(fileName)); 249 } 250 251 /* 252 * (non-Javadoc) 253 * 254 * @see org.graphstream.stream.file.FileSource#readAll(java.net.URL) 255 */ 256 public void readAll(URL url) throws IOException { 257 readAll(url.openStream()); 258 } 259 260 /* 261 * (non-Javadoc) 262 * 263 * @see org.graphstream.stream.file.FileSource#readAll(java.io.InputStream) 264 */ 265 public void readAll(InputStream stream) throws IOException { 266 readAll(new InputStreamReader(stream)); 267 } 268 269 /* 270 * (non-Javadoc) 271 * 272 * @see org.graphstream.stream.file.FileSource#readAll(java.io.Reader) 273 */ 274 public void readAll(Reader reader) throws IOException { 275 begin(reader); 276 while (nextEvents()) 277 ; 278 end(); 279 } 280 281 /* 282 * (non-Javadoc) 283 * 284 * @see org.graphstream.stream.file.FileSource#begin(java.lang.String) 285 */ 286 public void begin(String fileName) throws IOException { 287 begin(new FileReader(fileName)); 288 } 289 290 /* 291 * (non-Javadoc) 292 * 293 * @see org.graphstream.stream.file.FileSource#begin(java.net.URL) 294 */ 295 public void begin(URL url) throws IOException { 296 begin(url.openStream()); 297 } 298 299 /* 300 * (non-Javadoc) 301 * 302 * @see org.graphstream.stream.file.FileSource#begin(java.io.InputStream) 303 */ 304 public void begin(InputStream stream) throws IOException { 305 begin(new InputStreamReader(stream)); 306 } 307 308 /* 309 * (non-Javadoc) 310 * 311 * @see org.graphstream.stream.file.FileSource#begin(java.io.Reader) 312 */ 313 public void begin(Reader reader) throws IOException { 314 openStream(reader); 315 } 316 317 /* 318 * (non-Javadoc) 319 * 320 * @see org.graphstream.stream.file.FileSource#nextEvents() 321 */ 322 public boolean nextEvents() throws IOException { 323 try { 324 __graphml(); 325 } catch (XMLStreamException ex) { 326 throw new IOException(ex); 327 } 328 329 return false; 330 } 331 332 /* 333 * (non-Javadoc) 334 * 335 * @see org.graphstream.stream.file.FileSource#nextStep() 336 */ 337 public boolean nextStep() throws IOException { 338 return nextEvents(); 339 } 340 341 /* 342 * (non-Javadoc) 343 * 344 * @see org.graphstream.stream.file.FileSource#end() 345 */ 346 public void end() throws IOException { 347 closeStream(); 348 } 349 350 protected XMLEvent getNextEvent() throws IOException, XMLStreamException { 351 skipWhiteSpaces(); 352 353 if (events.size() > 0) 354 return events.pop(); 355 356 return reader.nextEvent(); 357 } 358 359 protected void pushback(XMLEvent e) { 360 events.push(e); 361 } 362 363 private XMLStreamException newParseError(XMLEvent e, String msg, 364 Object... args) { 365 return new XMLStreamException(String.format(msg, args), e.getLocation()); 366 } 367 368 private boolean isEvent(XMLEvent e, int type, String name) { 369 boolean valid = e.getEventType() == type; 370 371 if (valid) { 372 switch (type) { 373 case START_ELEMENT: 374 valid = e.asStartElement().getName().getLocalPart() 375 .equals(name); 376 break; 377 case END_ELEMENT: 378 valid = e.asEndElement().getName().getLocalPart().equals(name); 379 break; 380 case ATTRIBUTE: 381 valid = ((Attribute) e).getName().getLocalPart().equals(name); 382 break; 383 case CHARACTERS: 384 case NAMESPACE: 385 case PROCESSING_INSTRUCTION: 386 case COMMENT: 387 case START_DOCUMENT: 388 case END_DOCUMENT: 389 case DTD: 390 } 391 } 392 393 return valid; 394 } 395 396 private void checkValid(XMLEvent e, int type, String name) 397 throws XMLStreamException { 398 boolean valid = isEvent(e, type, name); 399 400 if (!valid) 401 throw newParseError(e, "expecting %s, got %s", gotWhat(type, name), 402 gotWhat(e)); 403 } 404 405 private String gotWhat(XMLEvent e) { 406 String v = null; 407 408 switch (e.getEventType()) { 409 case START_ELEMENT: 410 v = e.asStartElement().getName().getLocalPart(); 411 break; 412 case END_ELEMENT: 413 v = e.asEndElement().getName().getLocalPart(); 414 break; 415 case ATTRIBUTE: 416 v = ((Attribute) e).getName().getLocalPart(); 417 break; 418 } 419 420 return gotWhat(e.getEventType(), v); 421 } 422 423 private String gotWhat(int type, String v) { 424 switch (type) { 425 case START_ELEMENT: 426 return String.format("'<%s>'", v); 427 case END_ELEMENT: 428 return String.format("'</%s>'", v); 429 case ATTRIBUTE: 430 return String.format("attribute '%s'", v); 431 case NAMESPACE: 432 return "namespace"; 433 case PROCESSING_INSTRUCTION: 434 return "processing instruction"; 435 case COMMENT: 436 return "comment"; 437 case START_DOCUMENT: 438 return "document start"; 439 case END_DOCUMENT: 440 return "document end"; 441 case DTD: 442 return "dtd"; 443 case CHARACTERS: 444 return "characters"; 445 default: 446 return "UNKNOWN"; 447 } 448 } 449 450 private Object getValue(Data data) { 451 switch (data.key.type) { 452 case BOOLEAN: 453 return Boolean.parseBoolean(data.value); 454 case INT: 455 return Integer.parseInt(data.value); 456 case LONG: 457 return Long.parseLong(data.value); 458 case FLOAT: 459 return Float.parseFloat(data.value); 460 case DOUBLE: 461 return Double.parseDouble(data.value); 462 case STRING: 463 return data.value; 464 } 465 466 return data.value; 467 } 468 469 private Object getDefaultValue(Key key) { 470 switch (key.type) { 471 case BOOLEAN: 472 return Boolean.TRUE; 473 case INT: 474 if (key.def != null) 475 return Integer.valueOf(key.def); 476 477 return Integer.valueOf(0); 478 case LONG: 479 if (key.def != null) 480 return Long.valueOf(key.def); 481 482 return Long.valueOf(0); 483 case FLOAT: 484 if (key.def != null) 485 return Float.valueOf(key.def); 486 487 return Float.valueOf(0.0f); 488 case DOUBLE: 489 if (key.def != null) 490 return Double.valueOf(key.def); 491 492 return Double.valueOf(0.0); 493 case STRING: 494 if (key.def != null) 495 return key.def; 496 497 return ""; 498 } 499 500 return key.def != null ? key.def : Boolean.TRUE; 501 } 502 503 private void skipWhiteSpaces() throws IOException, XMLStreamException { 504 XMLEvent e; 505 506 do { 507 if (events.size() > 0) 508 e = events.pop(); 509 else 510 e = reader.nextEvent(); 511 } while (isEvent(e, XMLEvent.CHARACTERS, null) 512 && e.asCharacters().getData().matches("^\\s*$")); 513 514 pushback(e); 515 } 516 517 protected void openStream(Reader stream) throws IOException { 518 if (reader != null) 519 closeStream(); 520 521 try { 522 XMLEvent e; 523 524 reader = XMLInputFactory.newInstance().createXMLEventReader(stream); 525 526 e = getNextEvent(); 527 checkValid(e, XMLEvent.START_DOCUMENT, null); 528 529 } catch (XMLStreamException e) { 530 throw new IOException(e); 531 } catch (FactoryConfigurationError e) { 532 throw new IOException(e); 533 } 534 } 535 536 protected void closeStream() throws IOException { 537 try { 538 reader.close(); 539 } catch (XMLStreamException e) { 540 throw new IOException(e); 541 } finally { 542 reader = null; 543 } 544 } 545 546 protected String toConstantName(Attribute a) { 547 return toConstantName(a.getName().getLocalPart()); 548 } 549 550 protected String toConstantName(String value) { 551 return value.toUpperCase().replaceAll("\\W", "_"); 552 } 553 554 /** 555 * <pre> 556 * <!ELEMENT graphml ((desc)?,(key)*,((data)|(graph))*)> 557 * </pre> 558 * 559 * @throws IOException 560 * @throws XMLStreamException 561 */ 562 private void __graphml() throws IOException, XMLStreamException { 563 XMLEvent e; 564 565 e = getNextEvent(); 566 checkValid(e, XMLEvent.START_ELEMENT, "graphml"); 567 568 e = getNextEvent(); 569 570 if (isEvent(e, XMLEvent.START_ELEMENT, "desc")) { 571 pushback(e); 572 __desc(); 573 574 e = getNextEvent(); 575 } 576 577 while (isEvent(e, XMLEvent.START_ELEMENT, "key")) { 578 pushback(e); 579 __key(); 580 581 e = getNextEvent(); 582 } 583 584 while (isEvent(e, XMLEvent.START_ELEMENT, "data") 585 || isEvent(e, XMLEvent.START_ELEMENT, "graph")) { 586 pushback(e); 587 588 if (isEvent(e, XMLEvent.START_ELEMENT, "data")) { 589 __data(); 590 } else { 591 __graph(); 592 } 593 594 e = getNextEvent(); 595 } 596 597 checkValid(e, XMLEvent.END_ELEMENT, "graphml"); 598 } 599 600 private String __characters() throws IOException, XMLStreamException { 601 XMLEvent e; 602 StringBuilder buffer = new StringBuilder(); 603 604 e = getNextEvent(); 605 606 while (e.getEventType() == XMLEvent.CHARACTERS) { 607 buffer.append(e.asCharacters()); 608 e = getNextEvent(); 609 } 610 611 pushback(e); 612 613 return buffer.toString(); 614 } 615 616 /** 617 * <pre> 618 * <!ELEMENT desc (#PCDATA)> 619 * </pre> 620 * 621 * @return 622 * @throws IOException 623 * @throws XMLStreamException 624 */ 625 private String __desc() throws IOException, XMLStreamException { 626 XMLEvent e; 627 String desc; 628 629 e = getNextEvent(); 630 checkValid(e, XMLEvent.START_ELEMENT, "desc"); 631 632 desc = __characters(); 633 634 e = getNextEvent(); 635 checkValid(e, XMLEvent.END_ELEMENT, "desc"); 636 637 return desc; 638 } 639 640 /** 641 * <pre> 642 * <!ELEMENT locator EMPTY> 643 * <!ATTLIST locator 644 * xmlns:xlink CDATA #FIXED "http://www.w3.org/TR/2000/PR-xlink-20001220/" 645 * xlink:href CDATA #REQUIRED 646 * xlink:type (simple) #FIXED "simple" 647 * > 648 * </pre> 649 * 650 * @return 651 * @throws IOException 652 * @throws XMLStreamException 653 */ 654 private Locator __locator() throws IOException, XMLStreamException { 655 XMLEvent e; 656 657 e = getNextEvent(); 658 checkValid(e, XMLEvent.START_ELEMENT, "locator"); 659 660 @SuppressWarnings("unchecked") 661 Iterator<? extends Attribute> attributes = e.asStartElement() 662 .getAttributes(); 663 664 Locator loc = new Locator(); 665 666 while (attributes.hasNext()) { 667 Attribute a = attributes.next(); 668 669 try { 670 LocatorAttribute attribute = LocatorAttribute 671 .valueOf(toConstantName(a)); 672 673 switch (attribute) { 674 case XMLNS_XLINK: 675 loc.xlink = a.getValue(); 676 break; 677 case XLINK_HREF: 678 loc.href = a.getValue(); 679 break; 680 case XLINK_TYPE: 681 loc.type = a.getValue(); 682 break; 683 } 684 } catch (IllegalArgumentException ex) { 685 throw newParseError(e, "invalid locator attribute '%s'", a 686 .getName().getLocalPart()); 687 } 688 } 689 690 e = getNextEvent(); 691 checkValid(e, XMLEvent.END_ELEMENT, "locator"); 692 693 if (loc.href == null) 694 throw newParseError(e, "locator requires an href"); 695 696 return loc; 697 } 698 699 /** 700 * <pre> 701 * <!ELEMENT key (#PCDATA)> 702 * <!ATTLIST key 703 * id ID #REQUIRED 704 * for (graphml|graph|node|edge|hyperedge|port|endpoint|all) "all" 705 * > 706 * </pre> 707 * 708 * @throws IOException 709 * @throws XMLStreamException 710 */ 711 private void __key() throws IOException, XMLStreamException { 712 XMLEvent e; 713 714 e = getNextEvent(); 715 checkValid(e, XMLEvent.START_ELEMENT, "key"); 716 717 @SuppressWarnings("unchecked") 718 Iterator<? extends Attribute> attributes = e.asStartElement() 719 .getAttributes(); 720 721 String id = null; 722 KeyDomain domain = KeyDomain.ALL; 723 KeyAttrType type = KeyAttrType.STRING; 724 String name = null; 725 String def = null; 726 727 while (attributes.hasNext()) { 728 Attribute a = attributes.next(); 729 730 try { 731 KeyAttribute attribute = KeyAttribute 732 .valueOf(toConstantName(a)); 733 734 switch (attribute) { 735 case ID: 736 id = a.getValue(); 737 738 break; 739 case FOR: 740 try { 741 domain = KeyDomain 742 .valueOf(toConstantName(a.getValue())); 743 } catch (IllegalArgumentException ex) { 744 throw newParseError(e, "invalid key domain '%s'", 745 a.getValue()); 746 } 747 748 break; 749 case ATTR_TYPE: 750 try { 751 type = KeyAttrType 752 .valueOf(toConstantName(a.getValue())); 753 } catch (IllegalArgumentException ex) { 754 throw newParseError(e, "invalid key type '%s'", 755 a.getValue()); 756 } 757 758 break; 759 case ATTR_NAME: 760 name = a.getValue(); 761 762 break; 763 } 764 } catch (IllegalArgumentException ex) { 765 throw newParseError(e, "invalid key attribute '%s'", a 766 .getName().getLocalPart()); 767 } 768 } 769 770 e = getNextEvent(); 771 772 if (isEvent(e, XMLEvent.START_ELEMENT, "default")) { 773 def = __characters(); 774 775 e = getNextEvent(); 776 checkValid(e, XMLEvent.END_ELEMENT, "default"); 777 778 e = getNextEvent(); 779 } 780 781 checkValid(e, XMLEvent.END_ELEMENT, "key"); 782 783 if (id == null) 784 throw newParseError(e, "key requires an id"); 785 786 if (name == null) 787 name = id; 788 789 System.out.printf("add key \"%s\"\n", id); 790 791 Key k = new Key(); 792 k.name = name; 793 k.domain = domain; 794 k.type = type; 795 k.def = def; 796 797 keys.put(id, k); 798 } 799 800 /** 801 * <pre> 802 * <!ELEMENT port ((desc)?,((data)|(port))*)> 803 * <!ATTLIST port 804 * name NMTOKEN #REQUIRED 805 * > 806 * </pre> 807 * 808 * @return 809 * @throws IOException 810 * @throws XMLStreamException 811 */ 812 private Port __port() throws IOException, XMLStreamException { 813 XMLEvent e; 814 815 e = getNextEvent(); 816 checkValid(e, XMLEvent.START_ELEMENT, "port"); 817 818 Port port = new Port(); 819 @SuppressWarnings("unchecked") 820 Iterator<? extends Attribute> attributes = e.asStartElement() 821 .getAttributes(); 822 while (attributes.hasNext()) { 823 Attribute a = attributes.next(); 824 825 try { 826 PortAttribute attribute = PortAttribute 827 .valueOf(toConstantName(a)); 828 829 switch (attribute) { 830 case NAME: 831 port.name = a.getValue(); 832 break; 833 } 834 } catch (IllegalArgumentException ex) { 835 throw newParseError(e, "invalid attribute '%s' for '<port>'", a 836 .getName().getLocalPart()); 837 } 838 } 839 840 if (port.name == null) 841 throw newParseError(e, 842 "'<port>' element requires a 'name' attribute"); 843 844 e = getNextEvent(); 845 if (isEvent(e, XMLEvent.START_ELEMENT, "desc")) { 846 pushback(e); 847 port.desc = __desc(); 848 } else { 849 while (isEvent(e, XMLEvent.START_ELEMENT, "data") 850 || isEvent(e, XMLEvent.START_ELEMENT, "port")) { 851 if (isEvent(e, XMLEvent.START_ELEMENT, "data")) { 852 Data data; 853 854 pushback(e); 855 data = __data(); 856 857 port.datas.add(data); 858 } else { 859 Port portChild; 860 861 pushback(e); 862 portChild = __port(); 863 864 port.ports.add(portChild); 865 } 866 867 e = getNextEvent(); 868 } 869 } 870 871 e = getNextEvent(); 872 checkValid(e, XMLEvent.END_ELEMENT, "port"); 873 874 return port; 875 } 876 877 /** 878 * <pre> 879 * <!ELEMENT endpoint ((desc)?)> 880 * <!ATTLIST endpoint 881 * id ID #IMPLIED 882 * node IDREF #REQUIRED 883 * port NMTOKEN #IMPLIED 884 * type (in|out|undir) "undir" 885 * > 886 * </pre> 887 * 888 * @return 889 * @throws IOException 890 * @throws XMLStreamException 891 */ 892 private EndPoint __endpoint() throws IOException, XMLStreamException { 893 XMLEvent e; 894 895 e = getNextEvent(); 896 checkValid(e, XMLEvent.START_ELEMENT, "endpoint"); 897 898 @SuppressWarnings("unchecked") 899 Iterator<? extends Attribute> attributes = e.asStartElement() 900 .getAttributes(); 901 EndPoint ep = new EndPoint(); 902 903 while (attributes.hasNext()) { 904 Attribute a = attributes.next(); 905 906 try { 907 EndPointAttribute attribute = EndPointAttribute 908 .valueOf(toConstantName(a)); 909 910 switch (attribute) { 911 case NODE: 912 ep.node = a.getValue(); 913 break; 914 case ID: 915 ep.id = a.getValue(); 916 break; 917 case PORT: 918 ep.port = a.getValue(); 919 break; 920 case TYPE: 921 try { 922 ep.type = EndPointType.valueOf(toConstantName(a 923 .getValue())); 924 } catch (IllegalArgumentException ex) { 925 throw newParseError(e, "invalid end point type '%s'", 926 a.getValue()); 927 } 928 929 break; 930 } 931 } catch (IllegalArgumentException ex) { 932 throw newParseError(e, 933 "invalid attribute '%s' for '<endpoint>'", a.getName() 934 .getLocalPart()); 935 } 936 } 937 938 if (ep.node == null) 939 throw newParseError(e, 940 "'<endpoint>' element requires a 'node' attribute"); 941 942 e = getNextEvent(); 943 944 if (isEvent(e, XMLEvent.START_ELEMENT, "desc")) { 945 pushback(e); 946 ep.desc = __desc(); 947 } 948 949 e = getNextEvent(); 950 checkValid(e, XMLEvent.END_ELEMENT, "endpoint"); 951 952 return ep; 953 } 954 955 /** 956 * <pre> 957 * <!ELEMENT data (#PCDATA)> 958 * <!ATTLIST data 959 * key IDREF #REQUIRED 960 * id ID #IMPLIED 961 * > 962 * </pre> 963 * 964 * @return 965 * @throws IOException 966 * @throws XMLStreamException 967 */ 968 private Data __data() throws IOException, XMLStreamException { 969 XMLEvent e; 970 StringBuilder buffer = new StringBuilder(); 971 972 e = getNextEvent(); 973 checkValid(e, XMLEvent.START_ELEMENT, "data"); 974 975 @SuppressWarnings("unchecked") 976 Iterator<? extends Attribute> attributes = e.asStartElement() 977 .getAttributes(); 978 String key = null, id = null; 979 980 while (attributes.hasNext()) { 981 Attribute a = attributes.next(); 982 983 try { 984 DataAttribute attribute = DataAttribute 985 .valueOf(toConstantName(a)); 986 987 switch (attribute) { 988 case KEY: 989 key = a.getValue(); 990 break; 991 case ID: 992 id = a.getValue(); 993 break; 994 } 995 } catch (IllegalArgumentException ex) { 996 throw newParseError(e, "invalid attribute '%s' for '<data>'", a 997 .getName().getLocalPart()); 998 } 999 } 1000 1001 if (key == null) 1002 throw newParseError(e, 1003 "'<data>' element must have a 'key' attribute"); 1004 1005 e = getNextEvent(); 1006 1007 while (e.getEventType() == XMLEvent.CHARACTERS) { 1008 buffer.append(e.asCharacters()); 1009 e = getNextEvent(); 1010 } 1011 1012 checkValid(e, XMLEvent.END_ELEMENT, "data"); 1013 1014 if (keys.containsKey(key)) 1015 newParseError(e, "unknown key '%s'", key); 1016 1017 Data d = new Data(); 1018 1019 d.key = keys.get(key); 1020 d.id = id; 1021 d.value = buffer.toString(); 1022 1023 return d; 1024 } 1025 1026 /** 1027 * <pre> 1028 * <!ELEMENT graph ((desc)?,((((data)|(node)|(edge)|(hyperedge))*)|(locator)))> 1029 * <!ATTLIST graph 1030 * id ID #IMPLIED 1031 * edgedefault (directed|undirected) #REQUIRED 1032 * > 1033 * </pre> 1034 * 1035 * @throws IOException 1036 * @throws XMLStreamException 1037 */ 1038 private void __graph() throws IOException, XMLStreamException { 1039 XMLEvent e; 1040 1041 e = getNextEvent(); 1042 checkValid(e, XMLEvent.START_ELEMENT, "graph"); 1043 1044 @SuppressWarnings("unchecked") 1045 Iterator<? extends Attribute> attributes = e.asStartElement() 1046 .getAttributes(); 1047 1048 String id = null; 1049 String desc = null; 1050 boolean directed = false; 1051 boolean directedSet = false; 1052 1053 while (attributes.hasNext()) { 1054 Attribute a = attributes.next(); 1055 1056 try { 1057 GraphAttribute attribute = GraphAttribute 1058 .valueOf(toConstantName(a)); 1059 1060 switch (attribute) { 1061 case ID: 1062 id = a.getValue(); 1063 break; 1064 case EDGEDEFAULT: 1065 if (a.getValue().equals("directed")) 1066 directed = true; 1067 else if (a.getValue().equals("undirected")) 1068 directed = false; 1069 else 1070 throw newParseError(e, 1071 "invalid 'edgefault' value '%s'", a.getValue()); 1072 1073 directedSet = true; 1074 1075 break; 1076 } 1077 } catch (IllegalArgumentException ex) { 1078 throw newParseError(e, "invalid node attribute '%s'", a 1079 .getName().getLocalPart()); 1080 } 1081 } 1082 1083 if (!directedSet) 1084 throw newParseError(e, "graph requires attribute 'edgedefault'"); 1085 1086 String gid = ""; 1087 1088 if (graphId.size() > 0) 1089 gid = graphId.peek() + ":"; 1090 1091 if (id != null) 1092 gid += id; 1093 else 1094 gid += Integer.toString(graphCounter++); 1095 1096 graphId.push(gid); 1097 1098 e = getNextEvent(); 1099 1100 if (isEvent(e, XMLEvent.START_ELEMENT, "desc")) { 1101 pushback(e); 1102 desc = __desc(); 1103 1104 sendGraphAttributeAdded(sourceId, "desc", desc); 1105 1106 e = getNextEvent(); 1107 } 1108 1109 if (isEvent(e, XMLEvent.START_ELEMENT, "locator")) { 1110 pushback(e); 1111 __locator(); 1112 // TODO 1113 e = getNextEvent(); 1114 } else { 1115 while (isEvent(e, XMLEvent.START_ELEMENT, "data") 1116 || isEvent(e, XMLEvent.START_ELEMENT, "node") 1117 || isEvent(e, XMLEvent.START_ELEMENT, "edge") 1118 || isEvent(e, XMLEvent.START_ELEMENT, "hyperedge")) { 1119 pushback(e); 1120 1121 if (isEvent(e, XMLEvent.START_ELEMENT, "data")) { 1122 datas.add(__data()); 1123 } else if (isEvent(e, XMLEvent.START_ELEMENT, "node")) { 1124 __node(); 1125 } else if (isEvent(e, XMLEvent.START_ELEMENT, "edge")) { 1126 __edge(directed); 1127 } else { 1128 __hyperedge(); 1129 } 1130 1131 e = getNextEvent(); 1132 } 1133 } 1134 1135 graphId.pop(); 1136 checkValid(e, XMLEvent.END_ELEMENT, "graph"); 1137 } 1138 1139 /** 1140 * <pre> 1141 * <!ELEMENT node (desc?,(((data|port)*,graph?)|locator))> 1142 * <!ATTLIST node 1143 * id ID #REQUIRED 1144 * > 1145 * </pre> 1146 * 1147 * @throws IOException 1148 * @throws XMLStreamException 1149 */ 1150 private void __node() throws IOException, XMLStreamException { 1151 XMLEvent e; 1152 1153 e = getNextEvent(); 1154 checkValid(e, XMLEvent.START_ELEMENT, "node"); 1155 1156 @SuppressWarnings("unchecked") 1157 Iterator<? extends Attribute> attributes = e.asStartElement() 1158 .getAttributes(); 1159 1160 String id = null; 1161 HashSet<Key> sentAttributes = new HashSet<Key>(); 1162 1163 while (attributes.hasNext()) { 1164 Attribute a = attributes.next(); 1165 1166 try { 1167 NodeAttribute attribute = NodeAttribute 1168 .valueOf(toConstantName(a)); 1169 1170 switch (attribute) { 1171 case ID: 1172 id = a.getValue(); 1173 break; 1174 } 1175 } catch (IllegalArgumentException ex) { 1176 throw newParseError(e, "invalid node attribute '%s'", a 1177 .getName().getLocalPart()); 1178 } 1179 } 1180 1181 if (id == null) 1182 throw newParseError(e, "node requires an id"); 1183 1184 sendNodeAdded(sourceId, id); 1185 1186 e = getNextEvent(); 1187 1188 if (isEvent(e, XMLEvent.START_ELEMENT, "desc")) { 1189 String desc; 1190 1191 pushback(e); 1192 desc = __desc(); 1193 1194 sendNodeAttributeAdded(sourceId, id, "desc", desc); 1195 } else if (isEvent(e, XMLEvent.START_ELEMENT, "locator")) { 1196 // TODO 1197 pushback(e); 1198 __locator(); 1199 } else { 1200 while (isEvent(e, XMLEvent.START_ELEMENT, "data") 1201 || isEvent(e, XMLEvent.START_ELEMENT, "port")) { 1202 if (isEvent(e, XMLEvent.START_ELEMENT, "data")) { 1203 Data data; 1204 1205 pushback(e); 1206 data = __data(); 1207 1208 sendNodeAttributeAdded(sourceId, id, data.key.name, 1209 getValue(data)); 1210 1211 sentAttributes.add(data.key); 1212 } else { 1213 pushback(e); 1214 __port(); 1215 } 1216 1217 e = getNextEvent(); 1218 } 1219 } 1220 1221 for (Key k : keys.values()) { 1222 if ((k.domain == KeyDomain.NODE || k.domain == KeyDomain.ALL) 1223 && !sentAttributes.contains(k)) 1224 sendNodeAttributeAdded(sourceId, id, k.name, getDefaultValue(k)); 1225 } 1226 1227 if (isEvent(e, XMLEvent.START_ELEMENT, "graph")) { 1228 Location loc = e.getLocation(); 1229 1230 System.err.printf( 1231 "[WARNING] %d:%d graph inside node is not implemented", 1232 loc.getLineNumber(), loc.getColumnNumber()); 1233 1234 pushback(e); 1235 __graph(); 1236 1237 e = getNextEvent(); 1238 } 1239 1240 checkValid(e, XMLEvent.END_ELEMENT, "node"); 1241 } 1242 1243 /** 1244 * <pre> 1245 * <!ELEMENT edge ((desc)?,(data)*,(graph)?)> 1246 * <!ATTLIST edge 1247 * id ID #IMPLIED 1248 * source IDREF #REQUIRED 1249 * sourceport NMTOKEN #IMPLIED 1250 * target IDREF #REQUIRED 1251 * targetport NMTOKEN #IMPLIED 1252 * directed (true|false) #IMPLIED 1253 * > 1254 * </pre> 1255 * 1256 * @param edgedefault 1257 * @throws IOException 1258 * @throws XMLStreamException 1259 */ 1260 private void __edge(boolean edgedefault) throws IOException, 1261 XMLStreamException { 1262 XMLEvent e; 1263 1264 e = getNextEvent(); 1265 checkValid(e, XMLEvent.START_ELEMENT, "edge"); 1266 1267 @SuppressWarnings("unchecked") 1268 Iterator<? extends Attribute> attributes = e.asStartElement() 1269 .getAttributes(); 1270 1271 HashSet<Key> sentAttributes = new HashSet<Key>(); 1272 String id = null; 1273 boolean directed = edgedefault; 1274 String source = null; 1275 String target = null; 1276 1277 while (attributes.hasNext()) { 1278 Attribute a = attributes.next(); 1279 1280 try { 1281 EdgeAttribute attribute = EdgeAttribute 1282 .valueOf(toConstantName(a)); 1283 1284 switch (attribute) { 1285 case ID: 1286 id = a.getValue(); 1287 break; 1288 case DIRECTED: 1289 directed = Boolean.parseBoolean(a.getValue()); 1290 break; 1291 case SOURCE: 1292 source = a.getValue(); 1293 break; 1294 case TARGET: 1295 target = a.getValue(); 1296 break; 1297 case SOURCEPORT: 1298 case TARGETPORT: 1299 throw newParseError(e, 1300 "sourceport and targetport not implemented"); 1301 } 1302 } catch (IllegalArgumentException ex) { 1303 throw newParseError(e, "invalid graph attribute '%s'", a 1304 .getName().getLocalPart()); 1305 } 1306 } 1307 1308 if (id == null) 1309 throw newParseError(e, "edge must have an id"); 1310 1311 if (source == null || target == null) 1312 throw newParseError(e, "edge must have a source and a target"); 1313 1314 sendEdgeAdded(sourceId, id, source, target, directed); 1315 1316 e = getNextEvent(); 1317 1318 if (isEvent(e, XMLEvent.START_ELEMENT, "desc")) { 1319 String desc; 1320 1321 pushback(e); 1322 desc = __desc(); 1323 1324 sendEdgeAttributeAdded(sourceId, id, "desc", desc); 1325 } else { 1326 while (isEvent(e, XMLEvent.START_ELEMENT, "data")) { 1327 Data data; 1328 1329 pushback(e); 1330 data = __data(); 1331 1332 sendEdgeAttributeAdded(sourceId, id, data.key.name, 1333 getValue(data)); 1334 1335 sentAttributes.add(data.key); 1336 1337 e = getNextEvent(); 1338 } 1339 } 1340 1341 for (Key k : keys.values()) { 1342 if ((k.domain == KeyDomain.EDGE || k.domain == KeyDomain.ALL) 1343 && !sentAttributes.contains(k)) 1344 sendEdgeAttributeAdded(sourceId, id, k.name, getDefaultValue(k)); 1345 } 1346 1347 if (isEvent(e, XMLEvent.START_ELEMENT, "graph")) { 1348 Location loc = e.getLocation(); 1349 1350 System.err.printf( 1351 "[WARNING] %d:%d graph inside node is not implemented", 1352 loc.getLineNumber(), loc.getColumnNumber()); 1353 1354 pushback(e); 1355 __graph(); 1356 1357 e = getNextEvent(); 1358 } 1359 1360 checkValid(e, XMLEvent.END_ELEMENT, "edge"); 1361 } 1362 1363 /** 1364 * <pre> 1365 * <!ELEMENT hyperedge ((desc)?,((data)|(endpoint))*,(graph)?)> 1366 * <!ATTLIST hyperedge 1367 * id ID #IMPLIED 1368 * > 1369 * </pre> 1370 * 1371 * @throws IOException 1372 * @throws XMLStreamException 1373 */ 1374 private void __hyperedge() throws IOException, XMLStreamException { 1375 XMLEvent e; 1376 1377 e = getNextEvent(); 1378 checkValid(e, XMLEvent.START_ELEMENT, "hyperedge"); 1379 1380 Location loc = e.getLocation(); 1381 1382 System.err.printf( 1383 "[WARNING] %d:%d hyperedge feature is not implemented", 1384 loc.getLineNumber(), loc.getColumnNumber()); 1385 1386 String id = null; 1387 1388 @SuppressWarnings("unchecked") 1389 Iterator<? extends Attribute> attributes = e.asStartElement() 1390 .getAttributes(); 1391 1392 while (attributes.hasNext()) { 1393 Attribute a = attributes.next(); 1394 1395 try { 1396 HyperEdgeAttribute attribute = HyperEdgeAttribute 1397 .valueOf(toConstantName(a)); 1398 1399 switch (attribute) { 1400 case ID: 1401 id = a.getValue(); 1402 break; 1403 } 1404 } catch (IllegalArgumentException ex) { 1405 throw newParseError(e, 1406 "invalid attribute '%s' for '<endpoint>'", a.getName() 1407 .getLocalPart()); 1408 } 1409 } 1410 1411 if (id == null) 1412 throw newParseError(e, 1413 "'<hyperedge>' element requires a 'node' attribute"); 1414 1415 e = getNextEvent(); 1416 1417 if (isEvent(e, XMLEvent.START_ELEMENT, "desc")) { 1418 pushback(e); 1419 __desc(); 1420 } else { 1421 while (isEvent(e, XMLEvent.START_ELEMENT, "data") 1422 || isEvent(e, XMLEvent.START_ELEMENT, "endpoint")) { 1423 if (isEvent(e, XMLEvent.START_ELEMENT, "data")) { 1424 pushback(e); 1425 __data(); 1426 } else { 1427 pushback(e); 1428 __endpoint(); 1429 } 1430 1431 e = getNextEvent(); 1432 } 1433 } 1434 1435 if (isEvent(e, XMLEvent.START_ELEMENT, "graph")) { 1436 loc = e.getLocation(); 1437 1438 System.err.printf( 1439 "[WARNING] %d:%d graph inside node is not implemented", 1440 loc.getLineNumber(), loc.getColumnNumber()); 1441 1442 pushback(e); 1443 __graph(); 1444 1445 e = getNextEvent(); 1446 } 1447 1448 e = getNextEvent(); 1449 checkValid(e, XMLEvent.END_ELEMENT, "hyperedge"); 1450 } 1451}