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}