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

org.jgrapht.nio.gml.GmlEventDrivenImporter Maven / Gradle / Ivy

/*
 * (C) Copyright 2016-2021, by Dimitrios Michail and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.nio.gml;

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.misc.*;
import org.antlr.v4.runtime.tree.*;
import org.apache.commons.text.*;
import org.jgrapht.alg.util.Triple;
import org.jgrapht.nio.*;
import org.jgrapht.nio.gml.GmlParser.*;

import java.io.*;
import java.util.*;

/**
 * Imports a graph from a GML file (Graph Modeling Language).
 * 
 * 

* For a description of the format see * http://www.infosun.fmi.uni-passau.de/Graphlet/GML/. * *

* Below is small example of a graph in GML format. * *

 * graph [
 *   node [ 
 *     id 1
 *   ]
 *   node [
 *     id 2
 *     label "Node 2 has an optional label"
 *   ]
 *   node [
 *     id 3
 *   ]
 *   edge [
 *     source 1
 *     target 2 
 *     weight 2.0
 *     label "Edge between 1 and 2"
 *   ]
 *   edge [
 *     source 2
 *     target 3
 *     weight 3.0
 *     label "Edge between 2 and 3"
 *   ]
 * ]
 * 
* *

* If the input file contains edge weights then the importer also reads edge weights. The importer * also supports reading additional string attributes such as label or custom user attributes. * String attributes are unescaped as if they are Java strings. * *

* The parser completely ignores elements from the input that are not related to vertices or edges * of the graph. Moreover, complicated nested structures are simply returned as a whole. For * example, in the following graph * *

 * graph [
 *   node [ 
 *     id 1
 *   ]
 *   node [ 
 *     id 2
 *   ]
 *   edge [
 *     source 1
 *     target 2 
 *     points [ x 1.0 y 2.0 ]
 *   ]
 * ]
 * 
* * the points attribute of the edge is returned as a string containing "[ x 1.0 y 2.0 ]". * * @author Dimitrios Michail */ public class GmlEventDrivenImporter extends BaseEventDrivenImporter> implements EventDrivenImporter> { /** * Constructs a new importer. */ public GmlEventDrivenImporter() { super(); } @Override public void importInput(Reader input) throws ImportException { try { ThrowingErrorListener errorListener = new ThrowingErrorListener(); // create lexer GmlLexer lexer = new GmlLexer(CharStreams.fromReader(input)); lexer.removeErrorListeners(); lexer.addErrorListener(errorListener); // create parser GmlParser parser = new GmlParser(new CommonTokenStream(lexer)); parser.removeErrorListeners(); parser.addErrorListener(errorListener); // Specify our entry point GmlContext graphContext = parser.gml(); // Walk it and attach our listener ParseTreeWalker walker = new ParseTreeWalker(); NotifyGmlListener listener = new NotifyGmlListener(); notifyImportEvent(ImportEvent.START); walker.walk(listener, graphContext); listener.notifySingletons(); notifyImportEvent(ImportEvent.END); } catch (IOException e) { throw new ImportException("Failed to import gml graph: " + e.getMessage(), e); } catch (ParseCancellationException pe) { throw new ImportException("Failed to import gml graph: " + pe.getMessage(), pe); } catch (IllegalArgumentException iae) { throw new ImportException("Failed to import gml graph: " + iae.getMessage(), iae); } } private class ThrowingErrorListener extends BaseErrorListener { @Override public void syntaxError( Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) throws ParseCancellationException { throw new ParseCancellationException( "line " + line + ":" + charPositionInLine + " " + msg); } } // notify from parse tree private class NotifyGmlListener extends GmlBaseListener { private static final String NODE = "node"; private static final String EDGE = "edge"; private static final String GRAPH = "graph"; private static final String WEIGHT = "weight"; private static final String ID = "id"; private static final String SOURCE = "source"; private static final String TARGET = "target"; // current state of parser private boolean insideGraph; private boolean insideNode; private boolean insideEdge; private int level; private Integer nodeId; private Integer sourceId; private Integer targetId; private Double weight; private Map attributes; private StringBuilder stringBuffer; private int maxNodeId; private List singletons; public void notifySingletons() { for (Singleton s : singletons) { maxNodeId++; notifyVertex(maxNodeId); for (String attrKey : s.attributes.keySet()) { notifyVertexAttribute(maxNodeId, attrKey, s.attributes.get(attrKey)); } } } @Override public void enterGml(GmlParser.GmlContext ctx) { insideGraph = false; insideNode = false; insideEdge = false; level = 0; singletons = new ArrayList<>(); maxNodeId = 0; } @Override public void enterNumberKeyValue(GmlParser.NumberKeyValueContext ctx) { if (!insideNode && !insideEdge) { return; } if (level < 2) { return; } String key = ctx.ID().getText(); String value = ctx.NUMBER().getText(); if (level == 2) { if (insideNode) { if (key.equals(ID)) { try { nodeId = Integer.parseInt(value); } catch (NumberFormatException e) { // ignore error } } else { attributes.put(key, parseNumberAttribute(value)); } } else { // insideEdge assert insideEdge; switch (key) { case SOURCE: try { sourceId = Integer.parseInt(value); } catch (NumberFormatException e) { // ignore error } break; case TARGET: try { targetId = Integer.parseInt(value); } catch (NumberFormatException e) { // ignore error } break; case WEIGHT: try { weight = Double.parseDouble(value); } catch (NumberFormatException e) { // ignore error } break; default: attributes.put(key, parseNumberAttribute(value)); } } } else { assert level >= 3; /* * Inside a list. We simply concatenate everything here to allow the user to do * something fancier in user-code. */ stringBuffer.append(' '); stringBuffer.append(key); stringBuffer.append(' '); stringBuffer.append(value); } } @Override public void enterListKeyValue(GmlParser.ListKeyValueContext ctx) { String key = ctx.ID().getText(); if (level == 0 && key.equals(GRAPH)) { insideGraph = true; } else if (level == 1 && insideGraph && key.equals(NODE)) { insideNode = true; nodeId = null; attributes = new HashMap<>(); } else if (level == 1 && insideGraph && key.equals(EDGE)) { insideEdge = true; sourceId = null; targetId = null; weight = null; attributes = new HashMap<>(); } else if (insideNode || insideEdge) { if (level == 2) { stringBuffer = new StringBuilder(); stringBuffer.append('['); } else if (level >= 3) { stringBuffer.append(' '); stringBuffer.append(key); stringBuffer.append(' '); stringBuffer.append('['); } } level++; } @Override public void exitListKeyValue(GmlParser.ListKeyValueContext ctx) { String key = ctx.ID().getText(); level--; if (level == 0 && key.equals(GRAPH)) { insideGraph = false; } else if (level == 1 && insideGraph && key.equals(NODE)) { if (nodeId == null) { singletons.add(new Singleton(attributes)); } else { notifyVertex(nodeId); for (String attrKey : attributes.keySet()) { notifyVertexAttribute(nodeId, attrKey, attributes.get(attrKey)); } maxNodeId = Math.max(maxNodeId, nodeId); } insideNode = false; attributes = null; } else if (level == 1 && insideGraph && key.equals(EDGE)) { if (sourceId != null && targetId != null) { Triple et = Triple.of(sourceId, targetId, weight); notifyEdge(et); if (weight != null) { notifyEdgeAttribute(et, WEIGHT, DefaultAttribute.createAttribute(weight)); } for (String attrKey : attributes.keySet()) { notifyEdgeAttribute(et, attrKey, attributes.get(attrKey)); } } insideEdge = false; attributes = null; } else if (insideNode || insideEdge) { if (level == 2) { stringBuffer.append(' '); stringBuffer.append(']'); attributes .put( key, new DefaultAttribute<>(stringBuffer.toString(), AttributeType.UNKNOWN)); stringBuffer = null; } else if (level >= 3) { stringBuffer.append(' '); stringBuffer.append(']'); } } } @Override public void enterStringKeyValue(GmlParser.StringKeyValueContext ctx) { if (!insideNode && !insideEdge) { return; } if (level < 2) { return; } String key = ctx.ID().getText(); String text = ctx.STRING().getText(); String noQuotes = text.subSequence(1, text.length() - 1).toString(); String unescapedText = StringEscapeUtils.unescapeJava(noQuotes); if (level == 2) { /* * Store attribute */ if (key.equals(ID)) { throw new IllegalArgumentException("Invalid type for attribute id: string"); } else if (key.equals(SOURCE)) { throw new IllegalArgumentException("Invalid type for attribute source: string"); } else if (key.equals(TARGET)) { throw new IllegalArgumentException("Invalid type for attribute target: string"); } else if (key.equals(WEIGHT)) { throw new IllegalArgumentException("Invalid type for attribute weight: string"); } attributes.put(key, DefaultAttribute.createAttribute(unescapedText)); } else if (level >= 3) { /* * Inside a list. We simply concatenate everything here to allow the user to do * something fancier in user-code. */ stringBuffer.append(' '); stringBuffer.append(key); stringBuffer.append(' '); stringBuffer.append(text); } } private Attribute parseNumberAttribute(String value) { try { return DefaultAttribute.createAttribute(Integer.parseInt(value, 10)); } catch (NumberFormatException e) { // ignore } try { return DefaultAttribute.createAttribute(Long.parseLong(value, 10)); } catch (NumberFormatException e) { // ignore } try { return DefaultAttribute.createAttribute(Double.parseDouble(value)); } catch (NumberFormatException e) { // ignore } return DefaultAttribute.createAttribute(value); } } private class Singleton { Map attributes; public Singleton(Map attributes) { this.attributes = attributes; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy