/* $Id: MCTraceXML2JConverter.java 399 2009-06-26 16:01:20Z jfaber $
*
* This file is part of the PEA tool set
*
* The PEA tool set is a collection of tools for
* Phase Event Automata (PEA). See
* http://csd.informatik.uni-oldenburg.de/projects/peatools.html
* for more information.
*
* Copyright (C) 2005-2006, Department for Computing Science,
* University of Oldenburg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package de.uni_freiburg.informatik.ultimate.lib.pea.modelchecking;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import de.uni_freiburg.informatik.ultimate.core.model.services.ILogger;
import de.uni_freiburg.informatik.ultimate.lib.pea.CDD;
import de.uni_freiburg.informatik.ultimate.lib.pea.CounterTrace;
import de.uni_freiburg.informatik.ultimate.lib.pea.EventDecision;
/**
* The class MCTraceXML2JConverter
converts a given MCTrace
-Element from XML to Java
* representation.
*
*
* @see de.uni_freiburg.informatik.ultimate.lib.pea.modelchecking.MCTrace
* @see ModelCheckForm.xsd
*
* @author Roland Meyer
*
*
*/
public class MCTraceXML2JConverter {
private static final String DEFAULT_LOGGER = "MCTraceXML2JConverter";
private ILogger logger = null;
private DOMParser parser = null;
private FormulaXML2JConverter formulaConverter = null;
/**
* Initialises the MCTraceXML2JConverter object. Takes as parameter a string that defines the loggername for the
* built in log4j logger. If the string is empty, the default name MCTraceXML2JConverter
is used. An
* application using a MCTraceXML2JConverter object has to make sure that the logger is initialised via
* PropertyConfigurator.configure()
.
*
* @param loggerName
* @see ILogger
* @see PropertyConfigurator
*/
public MCTraceXML2JConverter(final String loggerName, final boolean useZDecision) throws Exception {
if (loggerName.equals("")) {
logger = ILogger.getLogger(MCTraceXML2JConverter.DEFAULT_LOGGER);
} else {
logger = ILogger.getLogger(loggerName);
}
formulaConverter = new FormulaXML2JConverter(useZDecision);
initialiseParser();
}
/**
* Initialises the MCTraceXML2JConverter object with the default logger.
*/
public MCTraceXML2JConverter(final boolean useZDecision) throws Exception {
this("", useZDecision);
}
/**
* Initialises the built in Xerces2 DomParser. Parser is set to validate the given XML-File against its schema
*
* @throws org.xml.sax.SAXNotRecognizedException
* If the requested string does not match an available feature
* @throws org.xml.sax.SAXNotSupportedException
* If the requested feature cannot be set
*
* @see org.apache.xerces.parsers.DOMParser;
*/
private void initialiseParser() throws Exception {
parser = new DOMParser();
logger.debug("Trying to set parser feature \"" + XMLTags.PARSER_VALIDATION_FEATURE + "\"");
parser.setFeature(XMLTags.PARSER_VALIDATION_FEATURE, true);
logger.debug("Setting parser feature \"" + XMLTags.PARSER_VALIDATION_FEATURE + "\" successful");
logger.debug("Trying to set parser feature \"" + XMLTags.PARSER_SCHEMA_VALIDATION_FEATURE + "\"");
parser.setFeature(XMLTags.PARSER_SCHEMA_VALIDATION_FEATURE, false);
logger.debug("Setting parser feature \"" + XMLTags.PARSER_SCHEMA_VALIDATION_FEATURE + "\" successful");
}
/**
* Converts all traces in the given MCForm
element mbFormNode
. Immediately calls the
* buildTraceModel
method.
*
* @param mcFormNode
* Node for which the traces need to be converted
*/
public MCTrace[] convert(final Element mcFormNode) {
return buildTraceModel(mcFormNode);
}
/**
* Parses the given file. The top level XML element is supposed to be an MCForm
element. All
* MCTrace
elements inside the model checkable formula are converted to Java.
*
* @param file
* The formula to be converted, given as XML-file.
* @return The array of MCTrace[] objects
*
* @throws SAXException
* If the formula does not match its schema, an exception is raised
* @throws IOException
* If the file is not readable an exception is raised.
*
*/
public MCTrace[] convert(final String file) throws SAXException, IOException {
final Document document = parse(file);
return buildTraceModel(document.getDocumentElement());
}
/**
* Parses the given file. Throws an exception, if there are parsing errors or if the file cannot be accessed.
*/
private Document parse(final String file) throws SAXException, IOException {
logger.debug("Trying to parse file=\"" + file + "\"");
parser.parse(file);
logger.debug("Parsing file=\"" + file + "\" successful");
return parser.getDocument();
}
/**
* Builds the array of MCTrace
objects for a given model checkable formula.
*
*
* @param node
* The formula the model checkable traces need to be converted for
* @return MCTrace[] The array of model checkable traces.
*
*/
private MCTrace[] buildTraceModel(final Element node) {
final NodeList mcTraceNodes = node.getElementsByTagName(XMLTags.MCTRACE_TAG);
final int traceCount = mcTraceNodes.getLength();
logger.info("MCTraceCount = " + traceCount);
final MCTrace[] traces = new MCTrace[traceCount];
for (int i = 0; i < traceCount; i++) {
logger.info("Trying to build mcTrace " + i);
traces[i] = buildMCTrace((Element) mcTraceNodes.item(i));
logger.info("Building mcTrace " + i + " successful");
}
return traces;
}
/**
* Build up a Java MCTrace
object for a given MCTrace
XML element.
*
* @param node
* The given XML element
* @return MCTrace The corresponding Java object
*/
private MCTrace buildMCTrace(final Element node) {
final MCTrace mcTrace = new MCTrace();
if (node.hasAttribute(XMLTags.ENTRYSYNC_TAG)) {
final String entrySync = node.getAttribute(XMLTags.ENTRYSYNC_TAG);
if (entrySync.equals("")) {
throw new RuntimeException("Existing entry sync events are not allowed to have empty names");
}
mcTrace.setEntrySync(EventDecision.create(entrySync));
logger.info("EntrySync = " + entrySync);
}
final NodeList traceNodes = node.getElementsByTagName(XMLTags.TRACE_TAG);
if (traceNodes.getLength() != 1) {
throw new RuntimeException("Trace count != 1 is not allowed");
}
logger.info("Trying to build trace");
buildTrace((Element) traceNodes.item(0), mcTrace);
logger.info("Building trace successful");
final String exitSync = node.getAttribute(XMLTags.EXITSYNC_TAG);
if (exitSync.equals("")) {
throw new RuntimeException("Exit sync events are not allowed to be empty");
}
mcTrace.setExitSync(EventDecision.create(exitSync));
logger.info("ExitSync = " + exitSync);
return mcTrace;
}
/**
* Builds up a Java CounterTrace
object for a given Trace
XML element. Also saves the
* missingEvent
s after the last phase.
*
* @param node
* The given XML element
* @return MCTrace The Java object that is currently built up
*/
private void buildTrace(final Element node, final MCTrace trace) {
final NodeList children = node.getChildNodes();
final NodeList phaseNodes = node.getElementsByTagName(XMLTags.PHASE_TAG);
final int phaseCount = phaseNodes.getLength();
if (phaseCount == 0) {
throw new RuntimeException("A trace with 0 phases is not allowed");
}
logger.info("PhaseCount = " + phaseCount);
final CounterTrace.DCPhase[] phases = new CounterTrace.DCPhase[phaseCount];
int actPhase = 0;
logger.info("Trying to build phase " + actPhase);
phases[actPhase] = buildPhase(CDD.TRUE, (Element) phaseNodes.item(actPhase));
logger.info("Building phase " + actPhase + " successful");
actPhase++;
CDD entryEvents = CDD.TRUE;
Element actNode = (Element) phaseNodes.item(0);
int traceElementCounter;
for (traceElementCounter = 0; traceElementCounter < children.getLength()
&& actPhase < phaseCount; traceElementCounter++) {
if (!children.item(traceElementCounter).getNodeName().equals(XMLTags.PHASE_TAG)
&& !children.item(traceElementCounter).getNodeName().equals(XMLTags.EVENT_TAG)) {
continue;
}
if (children.item(traceElementCounter) == phaseNodes.item(0)) {
continue;
}
actNode = (Element) children.item(traceElementCounter);
final String actName = actNode.getNodeName();
// Add a Phase.
if (actName.equals(XMLTags.PHASE_TAG)) {
logger.info("Trying to build phase " + actPhase);
phases[actPhase] = buildPhase(entryEvents, actNode);
logger.info("EntryEvents = " + entryEvents);
logger.info("Building phase " + actPhase + " successful");
entryEvents = CDD.TRUE;
actPhase++;
}
// Add an Event
if (actName.equals(XMLTags.EVENT_TAG)) {
final CDD event = EventDecision.create(getNameAttribute(actNode));
if (getSpecAttribute(actNode)) {
entryEvents = entryEvents.and(event);
} else {
entryEvents = entryEvents.and(event.negate());
}
}
}
trace.setTrace(new CounterTrace(phases));
// Process missing events
CDD missingEvents = CDD.TRUE;
for (int k = traceElementCounter; k < children.getLength(); k++) {
if (!children.item(k).getNodeName().equals(XMLTags.EVENT_TAG)) {
continue;
}
actNode = (Element) children.item(k);
if (getSpecAttribute(actNode)) {
missingEvents = missingEvents.and(EventDecision.create(getNameAttribute(actNode)));
} else {
missingEvents = missingEvents.and(EventDecision.createNeg(getNameAttribute(actNode)));
}
}
logger.info("MissingEvents = " + missingEvents);
trace.setMissingEvents(missingEvents);
final boolean spec = getSpecAttribute(node);
trace.setSpec(spec);
logger.info("TraceSpec = " + spec);
}
/**
* Gives the name of an element. Raises an exception if the name is empty.
*/
private static String getNameAttribute(final Element element) {
final String name = element.getAttribute(XMLTags.NAME_Tag);
if (name.equals("")) {
throw new RuntimeException("Name is not allowed to be empty");
}
return name;
}
/**
* Build up a Java DCPhase
object for a given Phase
XML element. The use of
* allowEmpty
can be enabled in this method.
*
* @param phaseNode
* The given XML phase
* @return CounterTrace.DCPhase The corresponding Java object
*/
private CounterTrace.DCPhase buildPhase(final CDD entryEvents, final Element phaseNode) {
int boundType = CounterTrace.BOUND_NONE;
double bound = 0;
final NodeList timeBounds = phaseNode.getElementsByTagName(XMLTags.TIMEBOUND_TAG);
if (timeBounds.getLength() > 1) {
throw new RuntimeException("Time bound count > 1 is not allowed");
}
if (timeBounds.getLength() == 1) {
final Element timeBoundNode = (Element) timeBounds.item(0);
boundType = getOperator(timeBoundNode);
bound = getTimeBound(timeBoundNode);
}
final NodeList stateInvariantNodes = phaseNode.getElementsByTagName(XMLTags.STATEINVARIANT_TAG);
if (stateInvariantNodes.getLength() != 1) {
throw new RuntimeException("Stateinvariant count != 1 is not allowed");
}
final Element invNode = (Element) stateInvariantNodes.item(0);
final CDD inv = formulaConverter.convert(invNode);
final Set forbidden = new TreeSet<>();
final NodeList forbiddenEventNodes = phaseNode.getElementsByTagName(XMLTags.FORBIDDENEVENT_TAG);
if (forbiddenEventNodes.getLength() > 0) {
final int forbiddenEventCount = forbiddenEventNodes.getLength();
for (int i = 0; i < forbiddenEventCount; i++) {
final Element actForbiddenEvent = (Element) forbiddenEventNodes.item(i);
forbidden.add(getNameAttribute(actForbiddenEvent));
}
}
boolean allowEmpty = false;
if (phaseNode.hasAttribute(XMLTags.ALLOWEMPTY_TAG)) {
final String allowEmpyString = phaseNode.getAttribute(XMLTags.ALLOWEMPTY_TAG);
if (!allowEmpyString.equals(XMLTags.TRUE_CONST) && !allowEmpyString.equals(XMLTags.FALSE_CONST)) {
throw new RuntimeException("AllowEmpty attribute needs to be set to \"" + XMLTags.TRUE_CONST
+ "\" or \"" + XMLTags.FALSE_CONST + "\"");
}
allowEmpty = Boolean.parseBoolean(allowEmpyString);
}
final CounterTrace.DCPhase phase = new CounterTrace.DCPhase(entryEvents, inv, boundType, (int) bound, forbidden,
allowEmpty);
logger.info("PhaseBoundType = " + boundType);
logger.info("PhaseBound = " + (int) bound);
logger.info("PhaseInvariant = " + inv);
logger.info("ForbiddenEvents = " + forbidden);
logger.info("AllowEmpty = " + allowEmpty);
return phase;
}
/**
* Returns the bound of an element as double
*
* @param timeBound
* The XML element
* @return double The double value
*/
private static double getTimeBound(final Element timeBound) {
final String bound = timeBound.getAttribute(XMLTags.BOUND_TAG);
return (new Double(bound)).doubleValue();
}
/**
* Returns the operator of a XML TimeBound
element. Raises an exception if the operator is not valid.
*
* @param timeBound
* The XML element with the operator to be returned
* @return int The number for the operator in the CounterTrace
class.
*/
private static int getOperator(final Element timeBound) {
final String op = timeBound.getAttribute(XMLTags.OPERATOR_TAG);
if (op.equals(XMLTags.GREATEREQUAL_CONST)) {
return CounterTrace.BOUND_GREATEREQUAL;
} else if (op.equals(XMLTags.GREATER_CONST)) {
return CounterTrace.BOUND_GREATER;
} else if (op.equals(XMLTags.LESS_CONST)) {
return CounterTrace.BOUND_LESS;
} else if (op.equals(XMLTags.LESSEQUAL_CONST)) {
return CounterTrace.BOUND_LESSEQUAL;
}
throw new RuntimeException(
"Operator needs to be " + "\"" + XMLTags.GREATEREQUAL_CONST + "\", " + "\"" + XMLTags.GREATER_CONST
+ "\", " + "\"" + XMLTags.LESS_CONST + "\", or " + "\"" + XMLTags.LESSEQUAL_CONST + "\"");
}
/**
* Returns the spec attribute. Raises an exception if the attribute does not have the value true or false.
*
* @param element
* The XML element with spec attribute
* @return boolean The boolean representation of the spec attribute
*/
private static boolean getSpecAttribute(final Element element) {
final String spec = element.getAttribute(XMLTags.SPEC_TAG);
if (!spec.equals(XMLTags.TRUE_CONST) && !spec.equals(XMLTags.FALSE_CONST)) {
throw new RuntimeException("Spec value != \"true\" and != \"false\" not allowed");
}
return Boolean.parseBoolean(spec);
}
/**
* Used for testing.
*/
public static void main(final String[] args) {
try {
final MCTraceXML2JConverter fileParser = new MCTraceXML2JConverter(false);
fileParser.convert("file:/home/roland/Desktop/Arbeit/PUMLaut/ModelCheckForm/ModelCheckForm.xml");
} catch (final Exception e) {
System.out.println("Exception raised");
e.printStackTrace();
}
}
}