All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.drus.jpa.unit.neo4j.dataset.graphml.GraphMLReader Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2017-2017, by Dimitrios Michail and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * This program and the accompanying materials are dual-licensed under
 * either
 *
 * (a) the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation, or (at your option) any
 * later version.
 *
 * or (per the licensee's choosing)
 *
 * (b) the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation.
 */

package eu.drus.jpa.unit.neo4j.dataset.graphml;

import static com.google.common.base.Preconditions.checkArgument;

import java.io.Reader;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.jgrapht.Graph;
import org.jgrapht.WeightedGraph;
import org.jgrapht.ext.GraphImporter;
import org.jgrapht.ext.ImportException;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

public class GraphMLReader implements GraphImporter {

    private VertexProvider vertexProvider;
    private EdgeProvider edgeProvider;

    public GraphMLReader(final VertexProvider vertexProvider, final EdgeProvider edgeProvider) {
        checkArgument(vertexProvider != null, "Vertex provider cannot be null");
        checkArgument(edgeProvider != null, "Edge provider cannot be null");

        this.vertexProvider = vertexProvider;
        this.edgeProvider = edgeProvider;
    }

    @Override
    public void importGraph(final Graph graph, final Reader in) throws ImportException {
        try {
            final SAXParserFactory spf = SAXParserFactory.newInstance();
            spf.setNamespaceAware(true);
            final SAXParser saxParser = spf.newSAXParser();
            final XMLReader xmlReader = saxParser.getXMLReader();
            final GraphMLHandler handler = new GraphMLHandler<>(graph, vertexProvider, edgeProvider);
            xmlReader.setContentHandler(handler);
            xmlReader.setErrorHandler(handler);
            xmlReader.parse(new InputSource(in));
        } catch (final Exception se) {
            throw new ImportException("Failed to parse GraphML", se);
        }
    }

    private static class GraphMLHandler extends DefaultHandler {
        private static final String GRAPH = "graph";
        private static final String GRAPH_ID = "id";
        private static final String NODE = "node";
        private static final String NODE_ID = "id";
        private static final String EDGE = "edge";
        private static final String ALL = "all";
        private static final String EDGE_SOURCE = "source";
        private static final String EDGE_TARGET = "target";
        private static final String KEY = "key";
        private static final String KEY_FOR = "for";
        private static final String KEY_ATTR_NAME = "attr.name";
        private static final String KEY_ATTR_TYPE = "attr.type";
        private static final String KEY_ID = "id";
        private static final String DEFAULT = "default";
        private static final String DATA = "data";
        private static final String DATA_KEY = "key";

        private static final String EDGE_WEIGHT_ATTRIBUTE_NAME = "weight";

        // record state of parser
        private boolean insideDefault;
        private boolean insideData;

        // temporary state while reading elements
        // stack needed due to nested graphs in GraphML
        private Data currentData;
        private Key currentKey;
        private Deque currentGraphElement;

        // collect custom keys
        private Map nodeValidKeys;
        private Map edgeValidKeys;
        private Map graphNodes;
        private Graph g;
        private VertexProvider vertexProvider;
        private EdgeProvider edgeProvider;

        public GraphMLHandler(final Graph graph, final VertexProvider vertexProvider, final EdgeProvider edgeProvider) {
            this.g = graph;
            this.vertexProvider = vertexProvider;
            this.edgeProvider = edgeProvider;
        }

        @Override
        public void startDocument() throws SAXException {
            nodeValidKeys = new HashMap<>();
            edgeValidKeys = new HashMap<>();
            graphNodes = new HashMap<>();
            insideDefault = false;
            insideData = false;
            currentKey = null;
            currentData = null;
            currentGraphElement = new ArrayDeque<>();
            currentGraphElement.push(new GraphElement("graphml"));
        }

        @Override
        public void startElement(final String uri, final String localName, final String qName, final Attributes attributes)
                throws SAXException {
            switch (localName) {
            case GRAPH:
                currentGraphElement.push(new GraphElement(findAttribute(GRAPH_ID, attributes)));
                break;
            case NODE:
                currentGraphElement.push(new GraphElement(findAttribute(NODE_ID, attributes)));
                break;
            case EDGE:
                currentGraphElement.push(new GraphElement(findAttribute(EDGE_SOURCE, attributes), findAttribute(EDGE_TARGET, attributes)));
                break;
            case KEY:
                final String keyId = findAttribute(KEY_ID, attributes);
                final String keyAttrName = findAttribute(KEY_ATTR_NAME, attributes);
                final String keyAttrType = findAttribute(KEY_ATTR_TYPE, attributes);
                final String keyFor = findAttribute(KEY_FOR, attributes);
                currentKey = new Key(keyId, keyAttrName, AttributeType.create(keyAttrType), toKeyTarget(keyFor));
                break;
            case DEFAULT:
                insideDefault = true;
                break;
            case DATA:
                insideData = true;
                currentData = new Data(findAttribute(DATA_KEY, attributes), null);
                break;
            default:
                break;
            }
        }

        private KeyTarget toKeyTarget(final String keyFor) {
            if (NODE.equals(keyFor)) {
                return KeyTarget.NODE;
            } else if (EDGE.equals(keyFor)) {
                return KeyTarget.EDGE;
            } else if (ALL.equals(keyFor)) {
                return KeyTarget.ALL;
            }
            return null;
        }

        @Override
        public void endElement(final String uri, final String localName, final String qName) throws SAXException {
            switch (localName) {
            case GRAPH:
                currentGraphElement.pop();
                break;
            case NODE:
                addVertext(currentGraphElement.pop());
                break;
            case EDGE:
                addEdge(currentGraphElement.pop());
                break;
            case KEY:
                if (currentKey.isValid()) {
                    switch (currentKey.target) {
                    case NODE:
                        nodeValidKeys.put(currentKey.id, currentKey);
                        break;
                    case EDGE:
                        edgeValidKeys.put(currentKey.id, currentKey);
                        break;
                    case ALL:
                        nodeValidKeys.put(currentKey.id, currentKey);
                        edgeValidKeys.put(currentKey.id, currentKey);
                        break;
                    }
                }
                currentKey = null;
                break;
            case DEFAULT:
                insideDefault = false;
                break;
            case DATA:
                if (currentData.isValid()) {
                    currentGraphElement.peek().attributes.put(currentData.key, currentData.value);
                }
                insideData = false;
                currentData = null;
                break;
            default:
                break;
            }
        }

        private void addVertext(final GraphElement currentNode) throws SAXException {
            verifyCondition(!graphNodes.containsKey(currentNode.id1), "Node with id " + currentNode.id1 + " already exists");

            final V v = vertexProvider.buildVertex(currentNode.id1, getAttributes(currentNode, nodeValidKeys));

            graphNodes.put(currentNode.id1, v);
            g.addVertex(v);
        }

        private void addEdge(final GraphElement p) throws SAXException {
            // create edges
            final V from = graphNodes.get(p.id1);
            verifyCondition(from != null, "Source vertex " + p.id1 + " not found");

            final V to = graphNodes.get(p.id2);
            verifyCondition(to != null, "Target vertex " + p.id2 + " not found");

            // create attributes
            final Map attributes = getAttributes(p, edgeValidKeys);

            final E e = edgeProvider.buildEdge(from, to, "e_" + from + "_" + to, attributes);

            setEdgeWeightIfRequired(e, attributes);

            g.addEdge(from, to, e);
        }

        private void setEdgeWeightIfRequired(final E e, final Map attributes) {
            // special handling for weighted graphs
            if (g instanceof WeightedGraph && attributes.containsKey(EDGE_WEIGHT_ATTRIBUTE_NAME)) {
                try {
                    ((WeightedGraph) g).setEdgeWeight(e, Float.valueOf(attributes.get(EDGE_WEIGHT_ATTRIBUTE_NAME).getValue()));
                } catch (final NumberFormatException nfe) {
                    ((WeightedGraph) g).setEdgeWeight(e, getEdgeWeight());
                }
            }
        }

        private double getEdgeWeight() {
            for (final Key k : edgeValidKeys.values()) {
                if (k.attributeName.equals(EDGE_WEIGHT_ATTRIBUTE_NAME)) {
                    try {
                        if (k.defaultValue != null) {
                            return Double.parseDouble(k.defaultValue);
                        }
                    } catch (final NumberFormatException e) {
                        return WeightedGraph.DEFAULT_EDGE_WEIGHT;
                    }
                }
            }

            return WeightedGraph.DEFAULT_EDGE_WEIGHT;
        }

        private Map getAttributes(final GraphElement ge, final Map keyReferences) {
            final Map attributes = new HashMap<>();

            for (final Key keyReference : keyReferences.values()) {
                if (ge.attributes.containsKey(keyReference.id)) {
                    final Attribute attribute = new DefaultAttribute<>(ge.attributes.get(keyReference.id), keyReference.attributeType);
                    attributes.put(keyReference.attributeName, attribute);
                } else if (keyReference.defaultValue != null) {
                    final Attribute attribute = new DefaultAttribute<>(keyReference.defaultValue, keyReference.attributeType);
                    attributes.put(keyReference.attributeName, attribute);
                }
            }

            return attributes;
        }

        @Override
        public void characters(final char[] ch, final int start, final int length) throws SAXException {
            if (insideDefault) {
                currentKey.defaultValue = new String(ch, start, length);
            } else if (insideData) {
                currentData.value = new String(ch, start, length);
            }
        }

        @Override
        public void warning(final SAXParseException e) throws SAXException {
            throw e;
        }

        @Override
        public void error(final SAXParseException e) throws SAXException {
            throw e;
        }

        @Override
        public void fatalError(final SAXParseException e) throws SAXException {
            throw e;
        }

        private String findAttribute(final String localName, final Attributes attributes) {
            for (int i = 0; i < attributes.getLength(); i++) {
                final String attrLocalName = attributes.getLocalName(i);
                if (attrLocalName.equals(localName)) {
                    return attributes.getValue(i);
                }
            }
            return null;
        }

        private static void verifyCondition(final boolean expression, final String message) throws SAXException {
            if (!expression) {
                throw new SAXException(message);
            }
        }

    }

    // ----- Helper classes for storing partial parser results -----

    private enum KeyTarget {
        NODE,
        EDGE,
        ALL;
    }

    private static class Key {
        String id;
        AttributeType attributeType;
        String attributeName;
        String defaultValue;
        KeyTarget target;

        public Key(final String id, final String attributeName, final AttributeType attributeType, final KeyTarget target) {
            this.id = id;
            this.attributeName = attributeName;
            this.attributeType = attributeType;
            this.target = target;
        }

        public boolean isValid() {
            return id != null && attributeName != null && target != null && attributeType != null;
        }

    }

    private static class Data {
        String key;
        String value;

        public Data(final String key, final String value) {
            this.key = key;
            this.value = value;
        }

        public boolean isValid() {
            return key != null && value != null;
        }
    }

    private static class GraphElement {
        String id1;
        String id2;
        Map attributes;

        public GraphElement(final String id1) {
            this(id1, null);
        }

        public GraphElement(final String id1, final String id2) {
            this.id1 = id1;
            this.id2 = id2;
            attributes = new HashMap<>();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy