From 7da7e7d7a6a41de4b8914119573f7c166abeee40 Mon Sep 17 00:00:00 2001 From: Yann Thierry-Mieg Date: Wed, 3 Jul 2024 12:40:22 +0200 Subject: [PATCH] a parser without a PNMLFW dependency. --- .../gal/structural/pnml/PTNetHandler.java | 247 ++++++++++++++++++ .../move/gal/structural/pnml/PTNetReader.java | 56 ++++ 2 files changed, 303 insertions(+) create mode 100644 fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/pnml/PTNetHandler.java create mode 100644 fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/pnml/PTNetReader.java diff --git a/fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/pnml/PTNetHandler.java b/fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/pnml/PTNetHandler.java new file mode 100644 index 000000000..f7e764e36 --- /dev/null +++ b/fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/pnml/PTNetHandler.java @@ -0,0 +1,247 @@ +/** + * Copyright (c) 2006-2010 MoVe - Laboratoire d'Informatique de Paris 6 (LIP6). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Jean-Baptiste VORON (LIP6) - Project Head / Initial contributor + * Clément DÉMOULINS (LIP6) - Project Manager + * Yann THIERRY-MIEG (LIP6) + * + * Official contacts: + * coloane@lip6.fr + * http://coloane.lip6.fr + */ +package fr.lip6.move.gal.structural.pnml; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.logging.Logger; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import fr.lip6.move.gal.structural.SparsePetriNet; + +/** + * A class to parse a PT model from an PNML file. + * + * @author Yann Thierry-Mieg + */ +public class PTNetHandler extends DefaultHandler { + + static final String PTNET = "http://www.pnml.org/version-2009/grammar/ptnet"; + + private static final boolean IGNORE_NAMES = true; + + private final Logger logger = Logger.getLogger("fr.lip6.move.gal"); //$NON-NLS-1$ + + // context stack + private Stack stack = new Stack(); + + // object constructed + private SparsePetriNet net = new SparsePetriNet(); + + private enum NodeType { + PLACE, TRANSITION; + } + + private static record Node(NodeType type, int index) { + } + + private Map nodes = new LinkedHashMap(); + + private static class Arc { + String source; + String target; + int value; + + public Arc(String source, String target, int value) { + this.source = source; + this.target = target; + this.value = value; + } + } + + private List topatch = new ArrayList<>(); + + private String lastseen = null; + private boolean readtext = false; + + private Long lastint = null; + private boolean readint = false; + + // private NupnHandler nupnHandler; + + // private boolean doNupn = false; + private boolean inOpaqueToolSpecific = false; + + private boolean doIt = false; + + @Override + public void characters(char[] chars, int beg, int length) throws SAXException { + if (inOpaqueToolSpecific) { + return; + } else if (doIt) { + if (readtext) { + String laststr = new String(Arrays.copyOfRange(chars, beg, beg + length)); + lastseen = laststr; + } else if (readint) { + String laststr = new String(Arrays.copyOfRange(chars, beg, beg + length)); + lastint = Long.parseLong(laststr); + } + } + } + + /** {@inheritDoc} */ + @Override + public final void startElement(String uri, String localName, String baliseName, Attributes attributes) + throws SAXException { + if ("net".equals(baliseName)) { //$NON-NLS-1$ + net.setName(attributes.getValue("id")); + String type = attributes.getValue("type"); + if (PTNET.equals(type)) { + logger.info("Found a PT net."); + } else { + throw new IllegalArgumentException( + "Parser only supports ptnet grammar. Type " + type + " is not supported."); + } + stack.push(net); + } else if ("name".equals(baliseName)) { + readtext = true; + + } else if ("page".equals(baliseName)) { + // ignore pages + } else if ("place".equals(baliseName)) { + String id = normalizeName(attributes.getValue("id")); + int pindex = net.addPlace(id, 0); + Node place = new Node(NodeType.PLACE, pindex); + nodes.put(id, place); + stack.push(place); + } else if ("initialMarking".equals(baliseName)) { + readint = true; + } else if ("inscription".equals(baliseName)) { + readint = true; + } else if ("transition".equals(baliseName)) { + String id = normalizeName(attributes.getValue("id")); + int tindex = net.addTransition(id); + Node trans = new Node(NodeType.TRANSITION, tindex); + nodes.put(id, trans); + stack.push(trans); + } else if ("arc".equals(baliseName)) { + Arc arc = new Arc(attributes.getValue("source"), attributes.getValue("target"), 1); + topatch.add(arc); + stack.push(arc); + } else if ("toolspecific".equals(baliseName)) { + logger.warning("Skipping unknown tool specific annotation : " + attributes.getValue("tool")); + inOpaqueToolSpecific = true; + } else if ("text".equals(baliseName)) { + doIt = true; + } else if ("graphics".equals(baliseName) || "offset".equals(baliseName) || "position".equals(baliseName) + || "fill".equals(baliseName) || "line".equals(baliseName) || "dimension".equals(baliseName)) { + // skip + } else if ("pnml".equals(baliseName)) { + // skip + } else { + logger.warning("Unknown XML tag in source file: " + baliseName); //$NON-NLS-1$ + } + } + + public static String normalizeName(String text) { + String res = text.replace(' ', '_'); + res = res.replace('-', '_'); + res = res.replace('/', '_'); + res = res.replace('*', 'x'); + res = res.replace('=', '_'); + + return res; + } + + /** {@inheritDoc} */ + @Override + public final void endElement(String uri, String localName, String baliseName) throws SAXException { + // Balise MODEL + if ("toolspecific".equals(baliseName)) { + if (inOpaqueToolSpecific) { + inOpaqueToolSpecific = false; + } + } else if (inOpaqueToolSpecific) { + // skipping this stuff + return; + } else if ("net".equals(baliseName)) { //$NON-NLS-1$ + stack.pop(); + assert (stack.isEmpty()); + } else if ("name".equals(baliseName)) { //$NON-NLS-1$ + Object context = stack.peek(); + if (context instanceof SparsePetriNet spn) { + spn.setName(lastseen); + } else if (context instanceof Node node) { + if (IGNORE_NAMES) { + if (node.type == NodeType.PLACE) { + net.getPnames().set(node.index, normalizeName(lastseen)); + } else if (node.type == NodeType.TRANSITION) { + net.getTnames().set(node.index, normalizeName(lastseen)); + } + } + } else { + logger.warning("Unexpected name tag in source file: " + baliseName + " context =" //$NON-NLS-1$ + + context.getClass().getName()); + } + readtext = false; + lastseen = null; + } else if ("page".equals(baliseName)) { //$NON-NLS-1$ + } else if ("place".equals(baliseName)) { + stack.pop(); + } else if ("transition".equals(baliseName)) { + stack.pop(); + } else if ("arc".equals(baliseName)) { + stack.pop(); + } else if ("text".equals(baliseName)) { + doIt = false; + } else if ("initialMarking".equals(baliseName)) { + Node p = (Node) stack.peek(); + net.getMarks().set(p.index, Math.toIntExact(lastint)); + readint = false; + lastint = null; + } else if ("inscription".equals(baliseName)) { + Arc p = (Arc) stack.peek(); + p.value = Math.toIntExact(lastint); + readint = false; + lastint = null; + } else if ("graphics".equals(baliseName) || "offset".equals(baliseName) || "position".equals(baliseName) + || "fill".equals(baliseName) || "line".equals(baliseName) || "dimension".equals(baliseName)) { + // skip + } else if ("pnml".equals(baliseName)) { + // add all arcs + for (Arc arc : topatch) { + Node src = nodes.get(arc.source); + Node target = nodes.get(arc.target); + int value = arc.value; + if (src == null || target == null) { + throw new RuntimeException("Problem when linking arc " + arc); + } + if (src.type == NodeType.PLACE) { + net.addPreArc(src.index, target.index, value); + } else if (src.type == NodeType.TRANSITION) { + net.addPostArc(src.index, target.index, value); + } + } + } else { + logger.warning("Unknown XML tag in source file: " + baliseName); //$NON-NLS-1$ + } + } + + /** + * @return the net loaded from the XML file + */ + public SparsePetriNet getParseResult() { + return net; + } +} diff --git a/fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/pnml/PTNetReader.java b/fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/pnml/PTNetReader.java new file mode 100644 index 000000000..00a795103 --- /dev/null +++ b/fr.lip6.move.gal.structural/src/fr/lip6/move/gal/structural/pnml/PTNetReader.java @@ -0,0 +1,56 @@ +/** + * A fast SAX parser for PNML files that builds a Sparse Petri Net. + */ +package fr.lip6.move.gal.structural.pnml; + +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Logger; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.SAXException; + +import fr.lip6.move.gal.structural.SparsePetriNet; + +/** + * Classe regroupant les outils utiles au chargement d'un modèle à partir d'un fichier xml + */ +public final class PTNetReader { + private static final Logger LOGGER = Logger.getLogger("fr.lip6.move.gal"); //$NON-NLS-1$ + private SparsePetriNet net = null; + + /** + * @param stringBuffer URI du fichier XML contenant le modèle à charger + * @param formalism formalism read + * @return IGraph construit à partir du fichier XML + */ + public SparsePetriNet loadFromXML(InputStream in) throws IllegalArgumentException { + PTNetHandler modelHandler = new PTNetHandler(); + SAXParserFactory factory = SAXParserFactory.newInstance(); + + try { + SAXParser saxParser = factory.newSAXParser(); + long debut = System.currentTimeMillis(); + saxParser.parse(in, modelHandler); + LOGGER.info("Load time of PNML (sax parser for PT used): " + (System.currentTimeMillis() - debut) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$ + } catch (SAXException e) { + LOGGER.warning("Parse error while parsing toolspecific elements in pnml.\n details:"+ e.getMessage()); //$NON-NLS-1$ + e.printStackTrace(); + } catch (IOException e) { + LOGGER.warning("IO exception : " + e.getMessage()); //$NON-NLS-1$ + } catch (ParserConfigurationException e) { + LOGGER.warning("Error in parser configuration. " + e.getMessage()); //$NON-NLS-1$ + e.printStackTrace(); + } + net = modelHandler.getParseResult(); + return modelHandler.getParseResult(); + } + + public SparsePetriNet getNet() { + return net; + } + +}