package betsy.bpmn.validation;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import betsy.bpmn.model.BPMNAssertions;
import configuration.bpmn.BPMNProcessRepository;
import org.junit.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import pebl.benchmark.test.Test;
public class BPMNValidator {
private List<Test> processes;
private DocumentBuilder builder;
private XPath xpath;
private static final String[] ALLOWED_LOG_MESSAGES = getAllowedLogMessages();
public void validate() {
processes = new BPMNProcessRepository().getByName("ALL");
setUpXmlObjects();
assertNamingConventionsCorrect();
assertLogMessagesCorrect();
}
private void assertNamingConventionsCorrect() {
for (Test process : processes) {
try {
Document doc = builder.parse(process.getProcess().toString());
XPathExpression definitionExpression = xpath.compile("//*[local-name() = 'definitions' and @id='" + process.getName() + "Test']");
NodeList nodeList = (NodeList) definitionExpression.evaluate(doc, XPathConstants.NODESET);
if (nodeList.getLength() != 1) {
throw new IllegalStateException("No definitions element with id '" + process.getName() + "Test' found in process " + process.getName());
}
XPathExpression processExpression = xpath.compile("//*[local-name() = 'process' and @id='" + process.getName() + "']");
nodeList = (NodeList) processExpression.evaluate(doc, XPathConstants.NODESET);
if (nodeList.getLength() != 1) {
throw new IllegalStateException("No process element with id '" + process.getName() + "' found in process " + process.getName());
}
String expectedTargetNamespace = "http://dsg.wiai.uniba.de/betsy/bpmn/" + process.getName();
XPathExpression targetNamespaceExpression = xpath.compile("//*[local-name() = 'definitions']/@targetNamespace");
nodeList = (NodeList) targetNamespaceExpression.evaluate(doc, XPathConstants.NODESET);
if (nodeList.getLength() != 1 && expectedTargetNamespace.equalsIgnoreCase(nodeList.item(0).getTextContent())) {
throw new IllegalStateException("targetNamespace of definitions element of process '" + process.getName() + " is not '" + expectedTargetNamespace + "'");
}
} catch (SAXException | XPathExpressionException | IOException e) {
throw new IllegalStateException("Validation failed for file " + process.getProcess().toString(), e);
}
}
}
private void assertLogMessagesCorrect() {
Set<String> messages = new HashSet<>();
for (Test process : processes) {
try {
Document doc = builder.parse(process.getProcess().toString());
XPathExpression definitionExpression = xpath.compile("//*[local-name() = 'script']");
NodeList nodeList = (NodeList) definitionExpression.evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
String textContent = nodeList.item(i).getTextContent();
addMessage(messages, process, textContent);
}
} catch (SAXException | XPathExpressionException | IOException e) {
throw new IllegalStateException("Validation failed for file " + process.getProcess().toString(), e);
}
}
String[] actualMessages = messages.toArray(new String[messages.size()]);
Arrays.sort(actualMessages);
Arrays.sort(ALLOWED_LOG_MESSAGES);
if (actualMessages.length < ALLOWED_LOG_MESSAGES.length) {
List<String> allowedMsgList = new LinkedList<>(Arrays.asList(ALLOWED_LOG_MESSAGES));
allowedMsgList.removeAll(Arrays.asList(actualMessages));
System.out.println("Allowed Log Messages " + allowedMsgList + " are not used anymore.");
} else if (actualMessages.length > ALLOWED_LOG_MESSAGES.length) {
List<String> actualMsgList = new LinkedList<>(Arrays.asList(actualMessages));
actualMsgList.removeAll(Arrays.asList(ALLOWED_LOG_MESSAGES));
System.out.println("Log Messages " + actualMsgList + " are used but not allowed anymore.");
}
Assert.assertArrayEquals(ALLOWED_LOG_MESSAGES, actualMessages);
}
private void addMessage(Set<String> assertions, Test process, String x) {
if (!Arrays.asList(ALLOWED_LOG_MESSAGES).contains(x)) {
System.out.println(x + " in " + process.getProcess());
}
assertions.add(x);
}
private void setUpXmlObjects() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
XPathFactory xPathfactory = XPathFactory.newInstance();
xpath = xPathfactory.newXPath();
xpath.setNamespaceContext(new BPMNNamespaceContext());
} catch (ParserConfigurationException e) {
throw new IllegalStateException("Could not validate process files", e);
}
}
public static String[] getAllowedLogMessages() {
List<String> allowedScriptTasks = BPMNAssertions.getScriptAssertions();
List<String> allowedLogMessages = new LinkedList<>();
allowedLogMessages.addAll(allowedScriptTasks);
allowedLogMessages.add("CREATE_LOG_FILE");
allowedLogMessages.add("CREATE_TIMESTAMP_LOG_1");
allowedLogMessages.add("CREATE_TIMESTAMP_LOG_2");
allowedLogMessages.add("CREATE_MARKER_FILE");
allowedLogMessages.add("WAIT_TEN_SECONDS");
allowedLogMessages.add("SET_STRING_DATA");
allowedLogMessages.add("LOG_DATA");
allowedLogMessages.add("THROW_ERROR");
allowedLogMessages.add("INCREMENT_INTEGER_VARIABLE");
allowedLogMessages.add("INCREMENT_INTEGER_VARIABLE_AND_LOG");
return allowedLogMessages.toArray(new String[allowedLogMessages.size()]);
}
}